Compare commits

..
16 Commits
Author SHA1 Message Date
ssongliu 67ce559b12 fix: 概览页磁盘信息获取增加锁 (#3100) 2023-11-29 15:34:51 +08:00
ssongliu 71869b7264 fix: 概览页磁盘显示优化 (#3096) 2023-11-29 14:44:02 +08:00
ssongliu 03774bbba6 fix: 概览页磁盘信息获取增加超时时间 (#3074)
Refs #2825
2023-11-28 15:10:35 +08:00
igophper e07b6bc050 fix: 类名重命名 2023-11-28 15:10:26 +08:00
igophper b1935d8368 fix: 修复网站404重定向次数过多 (#3054) (#3056)
Refs #3054
2023-11-28 15:10:17 +08:00
江杭辉 f8e344db76 fix: 修复登录菜单消失 2023-11-23 22:20:06 +08:00
江杭辉 2c3a8dd6af fix: 修复登录菜单消失 2023-11-23 22:20:01 +08:00
江杭辉 86e5fc36f4 fix: 修复登录菜单消失 2023-11-23 22:19:56 +08:00
江杭辉 858cefc0c9 fix: mfa登录菜单消失 2023-11-23 22:19:53 +08:00
ssongliu 4535c0b90b fix: 解决镜像仓库编辑失败的问题 (#3029) 2023-11-23 22:19:43 +08:00
zhengkunwang 136489e346 fix: 解决网站某些设置会报服务器内部错误的问题 (#2973)
Refs https://github.com/1Panel-dev/1Panel/issues/2955
2023-11-23 22:18:20 +08:00
wanghe e2882fd1cc Update sonar-project.properties (#3004) 2023-11-23 22:17:15 +08:00
hzz dba97076df fix: 本地数据备份命令增加 --default-character-set 参数 与备份还原的编码保持一致 2023-11-23 22:17:02 +08:00
QuentinHsu 000bcd37c1 fix: FileRole 初始化 (#2956) 2023-11-23 22:16:53 +08:00
wanghe bf28223745 Update README.md (#2937) 2023-11-23 22:16:47 +08:00
maninhill 1fed5c4d15 Update README.md 2023-11-23 22:16:42 +08:00
288 changed files with 4166 additions and 16002 deletions
+1 -1
View File
@@ -16,6 +16,6 @@ All security bugs should be reported to the contact as below:
- wanghe@fit2cloud.com
- zhengkun@fit2cloud.com
- support@fit2cloud.com
- +86 400-052-0755
- 400-052-0755
Thanks for your support!
+5
View File
@@ -6,6 +6,7 @@ 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/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
)
@@ -71,6 +72,10 @@ func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
checkData, err := appInstallService.CheckExist(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
-26
View File
@@ -1,8 +1,6 @@
package v1
import (
"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"
@@ -23,14 +21,6 @@ func (b *BaseApi) CreateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
key, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(key)
cert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -53,14 +43,6 @@ func (b *BaseApi) CheckDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
clientKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(clientKey)
clientCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(clientCert)
rootCert, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(rootCert)
}
helper.SuccessWithData(c, databaseService.CheckDatabase(req))
}
@@ -191,14 +173,6 @@ func (b *BaseApi) UpdateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
cKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(cKey)
cCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cCert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
-236
View File
@@ -1,236 +0,0 @@
package v1
import (
"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 Device
// @Summary Load device base info
// @Description 获取设备基础信息
// @Success 200 {object} dto.DeviceBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/device/base [post]
func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
data, err := deviceService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary list time zone options
// @Description 获取系统可用时区选项
// @Accept json
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/device/zone/options [get]
func (b *BaseApi) LoadTimeOption(c *gin.Context) {
list, err := deviceService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary load conf
// @Description 获取系统配置文件
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/conf [post]
func (b *BaseApi) LoadDeviceConf(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := deviceService.LoadConf(req.Name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary Update device conf by file
// @Description 通过文件修改配置
// @Accept json
// @Param request body dto.UpdateByNameAndFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/byconf [post]
func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {
var req dto.UpdateByNameAndFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateByConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device
// @Description 修改系统参数
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/conf [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机参数 [key] => [value]","formatEN":"update device conf [key] => [value]"}
func (b *BaseApi) UpdateDeviceConf(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.Update(req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device hosts
// @Description 修改系统 hosts
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/host [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机 Host [key] => [value]","formatEN":"update device host [key] => [value]"}
func (b *BaseApi) UpdateDeviceHost(c *gin.Context) {
var req []dto.HostHelper
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := deviceService.UpdateHosts(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device passwd
// @Description 修改系统密码
// @Accept json
// @Param request body dto.ChangePasswd true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/passwd [post]
func (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {
var req dto.ChangePasswd
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Passwd) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Passwd)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Passwd = string(password)
}
if err := deviceService.UpdatePasswd(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device swap
// @Description 修改系统 Swap
// @Accept json
// @Param request body dto.SwapHelper true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/swap [post]
// @x-panel-log {"bodyKeys":["operate","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机 swap [path]","formatEN":"[operate] device swap [path]"}
func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
var req dto.SwapHelper
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateSwap(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Check device DNS conf
// @Description 检查系统 DNS 配置可用性
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/check/dns [post]
func (b *BaseApi) CheckDNS(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := deviceService.CheckDNS(req.Key, req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, deviceService.Scan())
}
// @Tags Device
// @Summary Clean system
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
deviceService.Clean(req)
helper.SuccessWithData(c, nil)
}
+1 -24
View File
@@ -83,7 +83,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/logoption/update [post]
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
@@ -99,29 +99,6 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json ipv6 option
// @Description 修改 docker ipv6 配置
// @Accept json
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/ipv6option/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"}
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
var req dto.Ipv6Option
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := dockerService.UpdateIpv6Option(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json by upload file
// @Description 上传替换 docker 配置文件
-5
View File
@@ -33,9 +33,6 @@ var (
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
settingService = service.NewISettingService()
backupService = service.NewIBackupService()
@@ -59,6 +56,4 @@ var (
recycleBinService = service.NewIRecycleBinService()
favoriteService = service.NewIFavoriteService()
websiteCAService = service.NewIWebsiteCAService()
)
-153
View File
@@ -1,153 +0,0 @@
package v1
import (
"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/gin-gonic/gin"
)
// @Tags Fail2ban
// @Summary Load fail2ban base info
// @Description 获取 Fail2ban 基础信息
// @Success 200 {object} dto.Fail2BanBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/base [get]
func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {
data, err := fail2banService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Fail2ban
// @Summary Page fail2ban ip list
// @Description 获取 Fail2ban ip
// @Accept json
// @Param request body dto.Fail2BanSearch true "request"
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/search [post]
func (b *BaseApi) SearchFail2Ban(c *gin.Context) {
var req dto.Fail2BanSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := fail2banService.Search(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Fail2ban
// @Summary Operate fail2ban
// @Description 修改 Fail2ban 状态
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Fail2ban","formatEN":"[operation] Fail2ban"}
func (b *BaseApi) OperateFail2Ban(c *gin.Context) {
var req dto.Operate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.Operate(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Operate sshd of fail2ban
// @Description 配置 sshd
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate/sshd [post]
func (b *BaseApi) OperateSSHD(c *gin.Context) {
var req dto.Fail2BanSet
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.OperateSSHD(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Update fail2ban conf
// @Description 修改 Fail2ban 配置
// @Accept json
// @Param request body dto.Fail2BanUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 Fail2ban 配置 [key] => [value]","formatEN":"update fail2ban conf [key] => [value]"}
func (b *BaseApi) UpdateFail2BanConf(c *gin.Context) {
var req dto.Fail2BanUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Load fail2ban conf
// @Description 获取 fail2ban 配置文件
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/load/conf [get]
func (b *BaseApi) LoadFail2BanConf(c *gin.Context) {
path := "/etc/fail2ban/jail.local"
file, err := os.ReadFile(path)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, string(file))
}
// @Tags Fail2ban
// @Summary Update fail2ban conf by file
// @Description 通过文件修改 fail2ban 配置
// @Accept json
// @Param request body dto.UpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update/byconf [post]
func (b *BaseApi) UpdateFail2BanConfByFile(c *gin.Context) {
var req dto.UpdateByFile
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+8 -4
View File
@@ -685,21 +685,25 @@ func (b *BaseApi) Keys(c *gin.Context) {
// @Tags File
// @Summary Read file by Line
// @Description 按行读取日志文件
// @Description 按行读取文件
// @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 {
return
}
res, err := fileService.ReadLogByLine(req)
lines, end, err := files.ReadFileByLine(req.Path, req.Page, req.PageSize)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
res := response.FileLineContent{
End: end,
}
res.Path = req.Path
res.Content = strings.Join(lines, "\n")
helper.SuccessWithData(c, res)
}
-16
View File
@@ -68,19 +68,3 @@ func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags File
// @Summary Get RecycleBin status
// @Description 获取回收站状态
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /files/recycle/status [get]
func (b *BaseApi) GetRecycleStatus(c *gin.Context) {
settingInfo, err := settingService.GetSettingInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, settingInfo.FileRecycleBin)
}
+2 -3
View File
@@ -46,12 +46,11 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
ssl, err := runtimeService.Create(req)
if err != nil {
if err := runtimeService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, ssl)
helper.SuccessWithOutData(c)
}
// @Tags Website
+68
View File
@@ -217,6 +217,43 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load time zone options
// @Description 加载系统可用时区
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/time/option [get]
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
zones, err := settingService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, zones)
}
// @Tags System Setting
// @Summary Sync system time
// @Description 系统时间同步
// @Accept json
// @Param request body dto.SyncTime true "request"
// @Success 200 {string} ntime
// @Security ApiKeyAuth
// @Router /settings/time/sync [post]
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTime
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.SyncTime(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load local backup dir
// @Description 获取安装根目录
@@ -251,6 +288,37 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, settingService.SystemScan())
}
// @Tags System Setting
// @Summary System clean
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
settingService.SystemClean(req)
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load mfa info
// @Description 获取 mfa 信息
-144
View File
@@ -1,144 +0,0 @@
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 Website CA
// @Summary Page website ca
// @Description 获取网站 ca 列表分页
// @Accept json
// @Param request body request.WebsiteCASearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /websites/ca/search [post]
func (b *BaseApi) PageWebsiteCA(c *gin.Context) {
var req request.WebsiteCASearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, cas, err := websiteCAService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: cas,
})
}
// @Tags Website CA
// @Summary Create website ca
// @Description 创建网站 ca
// @Accept json
// @Param request body request.WebsiteCACreate true "request"
// @Success 200 {object} request.WebsiteCACreate
// @Security ApiKeyAuth
// @Router /websites/ca [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建网站 ca [name]","formatEN":"Create website ca [name]"}
func (b *BaseApi) CreateWebsiteCA(c *gin.Context) {
var req request.WebsiteCACreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := websiteCAService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Get website ca
// @Description 获取网站 ca
// @Accept json
// @Param id path int true "id"
// @Success 200 {object} response.WebsiteCADTO
// @Security ApiKeyAuth
// @Router /websites/ca/{id} [get]
func (b *BaseApi) GetWebsiteCA(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
return
}
res, err := websiteCAService.GetCA(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Delete website ca
// @Description 删除网站 ca
// @Accept json
// @Param request body request.WebsiteCommonReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/del [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":"删除网站 ca [name]","formatEN":"Delete website ca [name]"}
func (b *BaseApi) DeleteWebsiteCA(c *gin.Context) {
var req request.WebsiteCommonReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteCAService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 自签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/obtain [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) ObtainWebsiteCA(c *gin.Context) {
var req request.WebsiteCAObtain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 续签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/obtain [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
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(request.WebsiteCAObtain{
SSLID: req.SSLID,
Renew: true,
Unit: "year",
Time: 1,
}); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+13 -45
View File
@@ -1,10 +1,7 @@
package v1
import (
"net/http"
"net/url"
"reflect"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -69,24 +66,24 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
}
// @Tags Website SSL
// @Summary Apply ssl
// @Description 申请证书
// @Summary Reset website ssl
// @Description 重置网站 ssl
// @Accept json
// @Param request body request.WebsiteSSLApply true "request"
// @Param request body request.WebsiteSSLRenew true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/obtain [post]
// @x-panel-log {"bodyKeys":["ID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ID","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"申请证书 [domain]","formatEN":"apply ssl [domain]"}
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLApply
// @Router /websites/ssl/renew [post]
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLRenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.ObtainSSL(req); err != nil {
if err := websiteSSLService.Renew(req.SSLID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL
@@ -114,17 +111,17 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
// @Summary Delete website ssl
// @Description 删除网站 ssl
// @Accept json
// @Param request body request.WebsiteBatchDelReq true "request"
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
var req request.WebsiteBatchDelReq
var req request.WebsiteResourceReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.Delete(req.IDs); err != nil {
if err := websiteSSLService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -216,32 +213,3 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL
// @Summary Download SSL file
// @Description 下载证书文件
// @Accept json
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/download [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"下载证书文件 [domain]","formatEN":"download ssl file [domain]"}
func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
var req request.WebsiteResourceReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
file, err := websiteSSLService.DownloadFile(req.ID)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
info, err := file.Stat()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
}
-6
View File
@@ -13,18 +13,12 @@ type AppDatabase struct {
type AuthParam struct {
RootPassword string `json:"PANEL_DB_ROOT_PASSWORD"`
RootUser string `json:"PANEL_DB_ROOT_USER"`
}
type RedisAuthParam struct {
RootPassword string `json:"PANEL_REDIS_ROOT_PASSWORD"`
}
type MinioAuthParam struct {
RootPassword string `json:"PANEL_MINIO_ROOT_PASSWORD"`
RootUser string `json:"PANEL_MINIO_ROOT_USER"`
}
type ContainerExec struct {
ContainerName string `json:"containerName"`
DbParam AppDatabase `json:"dbParam"`
+2 -3
View File
@@ -31,7 +31,7 @@ type CommonBackup struct {
DetailName string `json:"detailName"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Name string `json:"name"`
DetailName string `json:"detailName"`
@@ -52,11 +52,10 @@ type BackupRecords struct {
BackupType string `json:"backupType"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
Size int64 `json:"size"`
}
type DownloadRecord struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
FileDir string `json:"fileDir" validate:"required"`
FileName string `json:"fileName" validate:"required"`
}
-9
View File
@@ -41,15 +41,6 @@ type DeleteByName struct {
Name string `json:"name" validate:"required"`
}
type UpdateByFile struct {
File string `json:"file"`
}
type UpdateByNameAndFile struct {
Name string `json:"name"`
File string `json:"file"`
}
type OperationWithNameAndType struct {
Name string `json:"name"`
Type string `json:"type" validate:"required"`
+7 -17
View File
@@ -52,7 +52,6 @@ type ContainerOperate struct {
CPUShares int64 `json:"cpuShares"`
NanoCPUs float64 `json:"nanoCPUs"`
Memory float64 `json:"memory"`
Privileged bool `json:"privileged"`
AutoRemove bool `json:"autoRemove"`
Volumes []VolumeHelper `json:"volumes"`
Labels []string `json:"labels"`
@@ -136,21 +135,13 @@ type Network struct {
Attachable bool `json:"attachable"`
}
type NetworkCreate struct {
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Ipv4 bool `json:"ipv4"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
AuxAddress []SettingUpdate `json:"auxAddress"`
Ipv6 bool `json:"ipv6"`
SubnetV6 string `json:"subnetV6"`
GatewayV6 string `json:"gatewayV6"`
IPRangeV6 string `json:"ipRangeV6"`
AuxAddressV6 []SettingUpdate `json:"auxAddressV6"`
Labels []string `json:"labels"`
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
Labels []string `json:"labels"`
}
type Volume struct {
@@ -168,7 +159,6 @@ type VolumeCreate struct {
}
type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
}
+1 -6
View File
@@ -43,12 +43,7 @@ type DashboardCurrent struct {
MemoryTotal uint64 `json:"memoryTotal"`
MemoryAvailable uint64 `json:"memoryAvailable"`
MemoryUsed uint64 `json:"memoryUsed"`
MemoryUsedPercent float64 `json:"memoryUsedPercent"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
SwapMemoryUsedPercent float64 `json:"swapMemoryUsedPercent"`
MemoryUsedPercent float64 `json:"MemoryUsedPercent"`
IOReadBytes uint64 `json:"ioReadBytes"`
IOWriteBytes uint64 `json:"ioWriteBytes"`
+26 -47
View File
@@ -227,24 +227,17 @@ type DatabaseSearch struct {
}
type DatabaseInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
}
type DatabaseOption struct {
@@ -257,39 +250,25 @@ type DatabaseOption struct {
}
type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
-41
View File
@@ -1,41 +0,0 @@
package dto
type DeviceBaseInfo struct {
DNS []string `json:"dns"`
Hosts []HostHelper `json:"hosts"`
Hostname string `json:"hostname"`
TimeZone string `json:"timeZone"`
LocalTime string `json:"localTime"`
Ntp string `json:"ntp"`
User string `json:"user"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
MaxSize uint64 `json:"maxSize"`
SwapDetails []SwapHelper `json:"swapDetails"`
}
type HostHelper struct {
IP string `json:"ip"`
Host string `json:"host"`
}
type SwapHelper struct {
Path string `json:"path" validate:"required"`
Size uint64 `json:"size"`
Used string `json:"used"`
IsNew bool `json:"isNew"`
}
type TimeZoneOptions struct {
From string `json:"from"`
Zones []string `json:"zones"`
}
type ChangePasswd struct {
User string `json:"user"`
Passwd string `json:"passwd"`
}
-11
View File
@@ -14,11 +14,6 @@ type DaemonJsonConf struct {
IPTables bool `json:"iptables"`
CgroupDriver string `json:"cgroupDriver"`
Ipv6 bool `json:"ipv6"`
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables"`
Experimental bool `json:"experimental"`
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
@@ -28,12 +23,6 @@ type LogOption struct {
LogMaxFile string `json:"logMaxFile"`
}
type Ipv6Option struct {
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables" validate:"required"`
Experimental bool `json:"experimental"`
}
type DockerOperation struct {
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
}
-29
View File
@@ -1,29 +0,0 @@
package dto
type Fail2BanBaseInfo struct {
IsEnable bool `json:"isEnable"`
IsActive bool `json:"isActive"`
IsExist bool `json:"isExist"`
Version string `json:"version"`
Port int `json:"port"`
MaxRetry int `json:"maxRetry"`
BanTime string `json:"banTime"`
FindTime string `json:"findTime"`
BanAction string `json:"banAction"`
LogPath string `json:"logPath"`
}
type Fail2BanSearch struct {
Status string `json:"status" validate:"required,oneof=banned ignore"`
}
type Fail2BanUpdate struct {
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath port"`
Value string `json:"value"`
}
type Fail2BanSet struct {
IPs []string `json:"ips"`
Operate string `json:"operate" validate:"required,oneof=banned ignore"`
}
+4 -7
View File
@@ -77,10 +77,9 @@ type FilePathCheck struct {
}
type FileWget struct {
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
IgnoreCertificate bool `json:"ignoreCertificate"`
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
}
type FileMove struct {
@@ -119,11 +118,9 @@ type FileRoleUpdate struct {
}
type FileReadByLineReq struct {
Path string `json:"path" validate:"required"`
Page int `json:"page" validate:"required"`
PageSize int `json:"pageSize" validate:"required"`
Type string `json:"type" validate:"required"`
ID uint `json:"ID"`
Name string `json:"name"`
}
type FileExistReq struct {
+1 -1
View File
@@ -210,7 +210,7 @@ type WebsiteRedirectReq struct {
type WebsiteWafFileUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Content string `json:"content" validate:"required"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block user_agent"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block"`
}
type WebsiteCommonReq struct {
+3 -57
View File
@@ -14,12 +14,6 @@ type WebsiteSSLCreate struct {
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
}
type WebsiteDNSReq struct {
@@ -31,16 +25,8 @@ type WebsiteSSLRenew struct {
SSLID uint `json:"SSLId" validate:"required"`
}
type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
}
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
Email string `json:"email" validate:"required"`
}
type WebsiteDnsAccountCreate struct {
@@ -60,14 +46,9 @@ type WebsiteResourceReq struct {
ID uint `json:"id" validate:"required"`
}
type WebsiteBatchDelReq struct {
IDs []uint `json:"ids" validate:"required"`
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
}
type WebsiteSSLUpload struct {
@@ -76,39 +57,4 @@ type WebsiteSSLUpload struct {
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
Type string `json:"type" validate:"required,oneof=paste local"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCASearch struct {
dto.PageInfo
}
type WebsiteCACreate struct {
CommonName string `json:"commonName" validate:"required"`
Country string `json:"country" validate:"required"`
Organization string `json:"organization" validate:"required"`
OrganizationUint string `json:"organizationUint"`
Name string `json:"name" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Province string `json:"province" `
City string `json:"city"`
}
type WebsiteCAObtain struct {
ID uint `json:"id" validate:"required"`
Domains string `json:"domains" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Time int `json:"time" validate:"required"`
Unit string `json:"unit" validate:"required"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
AutoRenew bool `json:"autoRenew"`
Renew bool `json:"renew"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCARenew struct {
SSLID uint `json:"SSLID" validate:"required"`
}
-11
View File
@@ -4,7 +4,6 @@ import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteSSLDTO struct {
model.WebsiteSSL
LogPath string `json:"logPath"`
}
type WebsiteDNSRes struct {
@@ -22,13 +21,3 @@ type WebsiteDnsAccountDTO struct {
model.WebsiteDnsAccount
Authorization map[string]string `json:"authorization"`
}
type WebsiteCADTO struct {
model.WebsiteCA
CommonName string `json:"commonName" `
Country string `json:"country"`
Organization string `json:"organization"`
OrganizationUint string `json:"organizationUint"`
Province string `json:"province" `
City string `json:"city"`
}
+6 -9
View File
@@ -3,11 +3,10 @@ package dto
import "time"
type SettingInfo struct {
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
DockerSockPath string `json:"dockerSockPath"`
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"`
@@ -50,8 +49,6 @@ type SettingInfo struct {
AppStoreVersion string `json:"appStoreVersion"`
AppStoreLastModified string `json:"appStoreLastModified"`
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
}
type SettingUpdate struct {
@@ -60,7 +57,7 @@ type SettingUpdate struct {
}
type SSLUpdate struct {
SSLType string `json:"sslType" validate:"required,oneof=self select import-paste import-local"`
SSLType string `json:"sslType"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
Cert string `json:"cert"`
@@ -100,7 +97,7 @@ 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"`
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"`
Description string `json:"description" validate:"max=256"`
}
type SnapshotRecover struct {
-17
View File
@@ -1,11 +1,5 @@
package model
import (
"github.com/1Panel-dev/1Panel/backend/constant"
"path/filepath"
"strings"
)
type App struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null"`
@@ -30,14 +24,3 @@ type App struct {
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
}
func (i *App) IsLocalApp() bool {
return i.Resource == constant.ResourceLocal
}
func (i *App) GetAppResourcePath() string {
if i.IsLocalApp() {
//这里要去掉本地应用的local前缀
return filepath.Join(constant.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
}
return filepath.Join(constant.RemoteAppResourceDir, i.Key)
}
+1 -8
View File
@@ -11,12 +11,5 @@ type Database struct {
Port uint `json:"port" gorm:"type:decimal;not null"`
Username string `json:"username" gorm:"type:varchar(64)"`
Password string `json:"password" gorm:"type:varchar(64)"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert" gorm:"type:longText"`
ClientKey string `json:"clientKey" gorm:"type:longText"`
ClientCert string `json:"clientCert" gorm:"type:longText"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description" gorm:"type:varchar(256);"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+3 -7
View File
@@ -2,13 +2,9 @@ package model
type WebsiteAcmeAccount struct {
BaseModel
Email string `gorm:"not null" json:"email"`
URL string `gorm:"not null" json:"url"`
PrivateKey string `gorm:"not null" json:"-"`
Type string `gorm:"not null;default:letsencrypt" json:"type"`
EabKid string `gorm:"default:null;" json:"eabKid"`
EabHmacKey string `gorm:"default:null" json:"eabHmacKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
Email string `gorm:"type:varchar(256);not null" json:"email"`
URL string `gorm:"type:varchar(256);not null" json:"url"`
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
}
func (w WebsiteAcmeAccount) TableName() string {
-9
View File
@@ -1,9 +0,0 @@
package model
type WebsiteCA struct {
BaseModel
CSR string `gorm:"not null;" json:"csr"`
Name string `gorm:"not null;" json:"name"`
PrivateKey string `gorm:"not null" json:"privateKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
}
+1 -18
View File
@@ -1,11 +1,6 @@
package model
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
"path"
"time"
)
import "time"
type WebsiteSSL struct {
BaseModel
@@ -19,26 +14,14 @@ type WebsiteSSL struct {
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `gorm:"not null;default:ready" json:"status"`
Message string `json:"message"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
Websites []Website `json:"websites" gorm:"-:migration"`
}
func (w WebsiteSSL) TableName() string {
return "website_ssls"
}
func (w WebsiteSSL) GetLogPath() string {
return path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", w.PrimaryDomain, w.ID))
}
+1 -1
View File
@@ -95,7 +95,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web
func (w *WebsiteRepo) List(opts ...DBOption) ([]model.Website, error) {
var websites []model.Website
err := getDb(opts...).Model(&model.Website{}).Preload("Domains").Preload("WebsiteSSL").Find(&websites).Error
err := getDb(opts...).Model(&model.Website{}).Preload("WebsiteSSL").Find(&websites).Error
return websites, err
}
-54
View File
@@ -1,54 +0,0 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type WebsiteCARepo struct {
}
func NewIWebsiteCARepo() IWebsiteCARepo {
return &WebsiteCARepo{}
}
type IWebsiteCARepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error)
GetFirst(opts ...DBOption) (model.WebsiteCA, error)
List(opts ...DBOption) ([]model.WebsiteCA, error)
Create(ctx context.Context, ca *model.WebsiteCA) error
DeleteBy(opts ...DBOption) error
}
func (w WebsiteCARepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&caList).Error
return count, caList, err
}
func (w WebsiteCARepo) GetFirst(opts ...DBOption) (model.WebsiteCA, error) {
var ca model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
if err := db.First(&ca).Error; err != nil {
return ca, err
}
return ca, nil
}
func (w WebsiteCARepo) List(opts ...DBOption) ([]model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
err := db.Find(&caList).Error
return caList, err
}
func (w WebsiteCARepo) Create(ctx context.Context, ca *model.WebsiteCA) error {
return getTx(ctx).Create(ca).Error
}
func (w WebsiteCARepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.WebsiteCA{}).Error
}
+8 -15
View File
@@ -14,12 +14,11 @@ type ISSLRepo interface {
WithByAlias(alias string) DBOption
WithByAcmeAccountId(acmeAccountId uint) DBOption
WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error)
Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl *model.WebsiteSSL) error
Save(ssl model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error
}
@@ -44,25 +43,19 @@ func (w WebsiteSSLRepo) WithByDnsAccountId(dnsAccountId uint) DBOption {
}
}
func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("DnsAccount").Preload("Websites").Find(&sslList).Error
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("Websites").Find(&sslList).Error
return count, sslList, err
}
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
var website *model.WebsiteSSL
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebsiteSSL, error) {
var website model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
if err := db.Preload("AcmeAccount").First(&website).Error; err != nil {
return website, err
}
return website, nil
@@ -71,7 +64,7 @@ func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
var websites []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Preload("DnsAccount").Find(&websites).Error; err != nil {
if err := db.Preload("AcmeAccount").Find(&websites).Error; err != nil {
return websites, err
}
return websites, nil
@@ -81,7 +74,7 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
return getTx(ctx).Create(ssl).Error
}
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
func (w WebsiteSSLRepo) Save(ssl model.WebsiteSSL) error {
return getDb().Save(&ssl).Error
}
+11 -12
View File
@@ -8,7 +8,7 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"path"
"reflect"
"strconv"
"strings"
@@ -61,7 +61,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
if req.Recommend {
opts = append(opts, appRepo.GetRecommend())
}
if req.Resource != "" && req.Resource != "all" {
if req.Resource != "" {
opts = append(opts, appRepo.WithResource(req.Resource))
}
if len(req.Tags) != 0 {
@@ -173,8 +173,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
fileOp := files.NewFileOp()
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
if !fileOp.Stat(versionPath) || detail.Update {
if err = downloadApp(app, detail, nil); err != nil {
return appDetailDTO, err
@@ -182,8 +181,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
switch app.Type {
case constant.RuntimePHP:
buildPath := filepath.Join(versionPath, "build")
paramsPath := filepath.Join(buildPath, "config.json")
buildPath := path.Join(versionPath, "build")
paramsPath := path.Join(buildPath, "config.json")
if !fileOp.Stat(paramsPath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -196,7 +195,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
appDetailDTO.Params = paramMap
composePath := filepath.Join(buildPath, "docker-compose.yml")
composePath := path.Join(buildPath, "docker-compose.yml")
if !fileOp.Stat(composePath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -224,7 +223,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
if appDetailDTO.DockerCompose == "" {
filename := filepath.Base(appDetailDTO.DownloadUrl)
filename := path.Base(appDetailDTO.DownloadUrl)
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
if err != nil {
@@ -490,7 +489,7 @@ func (a AppService) SyncAppListFromLocal() {
}
for _, dirEntry := range dirEntries {
if dirEntry.IsDir() {
appDir := filepath.Join(localAppDir, dirEntry.Name())
appDir := path.Join(localAppDir, dirEntry.Name())
appDirEntries, err := os.ReadDir(appDir)
if err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
@@ -508,7 +507,7 @@ func (a AppService) SyncAppListFromLocal() {
Version: appDirEntry.Name(),
Status: constant.AppNormal,
}
versionDir := filepath.Join(appDir, appDirEntry.Name())
versionDir := path.Join(appDir, appDirEntry.Name())
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
continue
@@ -743,7 +742,7 @@ func getAppFromRepo(downloadPath string) error {
downloadUrl := downloadPath
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
fileOp := files.NewFileOp()
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
return err
}
@@ -761,7 +760,7 @@ func getAppList() (*dto.AppList, error) {
if err := getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
return nil, err
}
listFile := filepath.Join(constant.ResourceDir, "1panel.json")
listFile := path.Join(constant.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
if err != nil {
return nil, err
-6
View File
@@ -8,7 +8,6 @@ import (
"os"
"path"
"reflect"
"sort"
"strconv"
"strings"
@@ -262,8 +261,6 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
return syncByID(install.ID)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup)
case constant.Reload:
return opNginx(install.ContainerName, constant.NginxReload)
default:
return errors.New("operate not support")
}
@@ -508,9 +505,6 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
})
}
}
sort.Slice(versions, func(i, j int) bool {
return common.CompareVersion(versions[i].Version, versions[j].Version)
})
return versions, nil
}
+72 -83
View File
@@ -118,13 +118,9 @@ var DatabaseKeys = map[string]uint{
"memcached": 11211,
}
var ToolKeys = map[string]uint{
"minio": 9001,
}
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
var dbConfig dto.AppDatabase
if DatabaseKeys[app.Key] > 0 {
if app.Type == "runtime" && DatabaseKeys[app.Key] > 0 {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
@@ -157,6 +153,14 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
case "mysql", "mariadb", "postgresql", "mongodb":
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.AuthParam{
RootPassword: password.(string),
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" {
database.Username = "root"
@@ -164,16 +168,6 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
authParam := dto.AuthParam{
RootPassword: password.(string),
RootUser: database.Username,
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
case "redis":
@@ -195,23 +189,6 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
return err
}
}
if ToolKeys[app.Key] > 0 {
if app.Key == "minio" {
authParam := dto.MinioAuthParam{}
if password, ok := params["PANEL_MINIO_ROOT_PASSWORD"]; ok {
authParam.RootPassword = password.(string)
}
if rootUser, ok := params["PANEL_MINIO_ROOT_USER"]; ok {
authParam.RootUser = rootUser.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
if app.Type == "website" || app.Type == "tool" {
paramByte, err := json.Marshal(params)
if err != nil {
@@ -359,6 +336,7 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
}
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if len(resources) == 0 {
return nil
@@ -405,66 +383,33 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
}
}
var upErr error
defer func() {
if upErr != nil {
if backup {
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
}
}
install.Status = constant.UpgradeErr
install.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &install)
}
}()
fileOp := files.NewFileOp()
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if install.App.Resource == constant.AppResourceRemote {
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
return
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
go func() {
_, _ = http.Get(detail.DownloadCallBackUrl)
}()
}
if install.App.Resource == constant.AppResourceLocal {
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 {
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
}
fileOp := files.NewFileOp()
sourceScripts := path.Join(detailDir, "scripts")
if fileOp.Stat(sourceScripts) {
dstScripts := path.Join(install.GetPath(), "scripts")
@@ -474,6 +419,16 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
_, _ = scriptCmd.CombinedOutput()
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
composeMap := make(map[string]interface{})
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
return
@@ -526,6 +481,24 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
install.Version = detail.Version
install.AppDetailId = detailId
images, err := getImages(install)
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 out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
@@ -587,6 +560,26 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
return containerNames, nil
}
func getImages(install model.AppInstall) ([]string, error) {
envStr, err := coverEnvJsonToStr(install.Env)
if err != nil {
return nil, err
}
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
if err != nil {
return nil, err
}
imagesMap := make(map[string]struct{})
for _, service := range project.AllServices() {
imagesMap[service.Image] = struct{}{}
}
var images []string
for k := range imagesMap {
images = append(images, k)
}
return images, nil
}
func coverEnvJsonToStr(envJson string) (string, error) {
envMap := make(map[string]interface{})
_ = json.Unmarshal([]byte(envJson), &envMap)
@@ -637,12 +630,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
}
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
if app.IsLocalApp() {
//本地应用,不去官网下载
return nil
}
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
appDownloadDir := app.GetAppResourcePath()
appDownloadDir := path.Join(appResourceDir, app.Key)
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
fileOp := files.NewFileOp()
if !appDetail.Update && fileOp.Stat(appVersionDir) {
@@ -908,28 +897,28 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
fileOp := files.NewFileOp()
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
if !fileOp.Stat(dockerComposePath) {
return buserr.WithName(constant.ErrFileNotFound, "docker-compose.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
}
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
if dockerComposeByte == nil {
return buserr.WithName(constant.ErrFileParseApp, "docker-compose.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
}
appDetail.DockerCompose = string(dockerComposeByte)
paramPath := path.Join(versionDir, "data.yml")
if !fileOp.Stat(paramPath) {
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
}
paramByte, _ := fileOp.GetContent(paramPath)
if paramByte == nil {
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
appParamConfig := dto.LocalAppParam{}
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
dataJson, err := json.Marshal(appParamConfig.AppParams)
if err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
}
appDetail.Params = string(dataJson)
return nil
@@ -939,22 +928,22 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
fileOp := files.NewFileOp()
configYamlPath := path.Join(appDir, "data.yml")
if !fileOp.Stat(configYamlPath) {
err = buserr.WithName(constant.ErrFileNotFound, "data.yml")
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
return
}
iconPath := path.Join(appDir, "logo.png")
if !fileOp.Stat(iconPath) {
err = buserr.WithName(constant.ErrFileNotFound, "logo.png")
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
return
}
configYamlByte, err := fileOp.GetContent(configYamlPath)
if err != nil {
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
localAppDefine := dto.LocalAppAppDefine{}
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
app = &localAppDefine.AppProperty
@@ -1163,7 +1152,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
if portExist {
allowHost := "127.0.0.1"
if req.Advanced && req.AllowPort {
allowHost = ""
allowHost = "0.0.0.0"
}
params[constant.HostIP] = allowHost
}
@@ -1175,7 +1164,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig
config := request.AppContainerConfig{}
if hostIp, ok := envs[constant.HostIP]; ok {
config.AllowPort = hostIp.(string) != "127.0.0.1"
config.AllowPort = hostIp.(string) == "0.0.0.0"
} else {
config.AllowPort = true
}
+13 -42
View File
@@ -69,7 +69,6 @@ func (u *BackupService) List() ([]dto.BackupInfo, error) {
dtobas = append(dtobas, u.loadByType("COS", ops))
dtobas = append(dtobas, u.loadByType("KODO", ops))
dtobas = append(dtobas, u.loadByType("OneDrive", ops))
dtobas = append(dtobas, u.loadByType("WebDAV", ops))
return dtobas, err
}
@@ -81,46 +80,15 @@ 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 dtobas []dto.BackupRecords
for _, group := range records {
var item dto.BackupRecords
if err := copier.Copy(&item, &records[i]); err != nil {
if err := copier.Copy(&item, &group); 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)
dtobas = append(dtobas, item)
}
return total, datas, err
}
type loadSizeHelper struct {
backupPath string
client cloud_storage.CloudStorageClient
return total, dtobas, err
}
func (u *BackupService) LoadOneDriveInfo() (string, error) {
@@ -149,7 +117,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -170,7 +138,9 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
if len(backup.BackupPath) != 0 {
srcPath = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), srcPath)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
srcPath = itemPath + srcPath
}
if exist, _ := backClient.Exist(srcPath); exist {
isOK, err := backClient.Download(srcPath, targetPath)
@@ -196,7 +166,8 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
}
}
if req.Type != "LOCAL" {
if _, err := u.checkBackupConn(&backup); err != nil {
isOk, err := u.checkBackupConn(&backup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
@@ -212,7 +183,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
return nil, err
}
switch backupDto.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backupDto.AccessKey
varMap["password"] = backupDto.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -357,7 +328,7 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
+18 -61
View File
@@ -2,12 +2,12 @@ package service
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
@@ -27,7 +27,6 @@ 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"
@@ -161,7 +160,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp = true
}
ports := loadContainerPort(item.Ports)
ports := simplifyPort(item.Ports)
backDatas[i] = dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
@@ -409,7 +408,6 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
if oldContainer.HostConfig.NanoCPUs != 0 {
@@ -443,7 +441,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
if !req.ForcePull {
return err
}
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
}
}
@@ -486,7 +484,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
if !req.ForcePull {
return err
}
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
}
}
config := oldContainer.Config
@@ -701,15 +699,18 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
filePath := ""
if req.Type == "compose-detail" {
cli, err := docker.NewDockerClient()
switch req.Type {
case "image-pull", "image-push", "image-build", "compose-create":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail":
client, err := docker.NewDockerClient()
if err != nil {
return ""
}
options := types.ContainerListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
containers, err := cli.ContainerList(context.Background(), options)
containers, err := client.ContainerList(context.Background(), options)
if err != nil {
return ""
}
@@ -724,6 +725,9 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
break
}
}
if req.Type == "compose-create" {
filePath = path.Join(path.Dir(filePath), "compose.log")
}
}
if _, err := os.Stat(filePath); err != nil {
return ""
@@ -807,25 +811,7 @@ func checkImageExist(client *client.Client, image string) bool {
}
func pullImages(ctx context.Context, client *client.Client, image string) error {
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)
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
return err
}
@@ -951,7 +937,6 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
networkConf = network.NetworkingConfig{}
}
hostConf.Privileged = req.Privileged
hostConf.AutoRemove = req.AutoRemove
hostConf.CPUShares = req.CPUShares
hostConf.PublishAllPorts = req.PublishAllPorts
@@ -1033,22 +1018,6 @@ func loadVolumeBinds(binds []string) []dto.VolumeHelper {
return datas
}
func loadContainerPort(ports []types.Port) []string {
var (
ipv4Ports []types.Port
ipv6Ports []types.Port
)
for _, port := range ports {
if strings.Contains(port.IP, ":") {
ipv6Ports = append(ipv6Ports, port)
} else {
ipv4Ports = append(ipv4Ports, port)
}
}
list1 := simplifyPort(ipv4Ports)
list2 := simplifyPort(ipv6Ports)
return append(list1, list2...)
}
func simplifyPort(ports []types.Port) []string {
var datas []string
if len(ports) == 0 {
@@ -1079,19 +1048,13 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i-1].PublicPort, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
}
start = ports[i]
}
@@ -1101,19 +1064,13 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i].PublicPort, start.PrivatePort, ports[i].PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
}
}
}
+3 -26
View File
@@ -216,8 +216,7 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
if cmd.CheckIllegal(req.Name, req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
oldFile, err := os.ReadFile(req.Path)
if err != nil {
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640)
@@ -231,16 +230,10 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
if stdout, err := compose.Down(req.Path); err != nil {
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose down, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose down, err: %s", string(stdout))
return errors.New(string(stdout))
}
if stdout, err := compose.Up(req.Path); err != nil {
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose up, err: %s", string(stdout))
return errors.New(string(stdout))
}
return nil
@@ -268,19 +261,3 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
}
return nil
}
func recreateCompose(content, path string) error {
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(content)
write.Flush()
if stdout, err := compose.Up(path); err != nil {
return errors.New(string(stdout))
}
return nil
}
+17 -45
View File
@@ -116,57 +116,29 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
return err
}
var (
ipams []network.IPAMConfig
enableV6 bool
ipam network.IPAMConfig
hasConf bool
)
if req.Ipv4 {
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.Subnet) != 0 {
itemIpam.Subnet = req.Subnet
}
if len(req.Gateway) != 0 {
itemIpam.Gateway = req.Gateway
}
if len(req.IPRange) != 0 {
itemIpam.IPRange = req.IPRange
}
for _, addr := range req.AuxAddress {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
if len(req.Subnet) != 0 {
ipam.Subnet = req.Subnet
hasConf = true
}
if req.Ipv6 {
enableV6 = true
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.SubnetV6) != 0 {
itemIpam.Subnet = req.SubnetV6
}
if len(req.GatewayV6) != 0 {
itemIpam.Gateway = req.GatewayV6
}
if len(req.IPRangeV6) != 0 {
itemIpam.IPRange = req.IPRangeV6
}
for _, addr := range req.AuxAddressV6 {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
if len(req.Gateway) != 0 {
ipam.Gateway = req.Gateway
hasConf = true
}
if len(req.IPRange) != 0 {
ipam.IPRange = req.IPRange
hasConf = true
}
options := types.NetworkCreate{
EnableIPv6: enableV6,
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
}
if len(ipams) != 0 {
options.IPAM = &network.IPAM{Config: ipams}
if hasConf {
options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}}
}
if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil {
return err
+3 -9
View File
@@ -9,7 +9,6 @@ import (
"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/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
@@ -52,20 +51,15 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
records = list.Volumes[start:end]
}
nyc, _ := time.LoadLocation(common.LoadTimeZone())
for _, item := range records {
tag := make([]string, 0)
for _, val := range item.Labels {
tag = append(tag, val)
}
var createTime time.Time
if strings.Contains(item.CreatedAt, "Z") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
} else if strings.Contains(item.CreatedAt, "+") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
} else {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
if len(item.CreatedAt) > 19 {
item.CreatedAt = item.CreatedAt[0:19]
}
createTime, _ := time.Parse("2006-01-02T15:04:05", item.CreatedAt)
data = append(data, dto.Volume{
CreatedAt: createTime,
Name: item.Name,
+16 -6
View File
@@ -157,7 +157,9 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
return "", err
}
if len(backup.BackupPath) != 0 {
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return "", err
@@ -191,7 +193,9 @@ func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string,
fileItem := file
if cronjob.KeepLocal {
if len(backupPath) != 0 {
fileItem = path.Join(strings.TrimPrefix(backupPath, "/") + strings.TrimPrefix(file, localDir+"/"))
itemPath := strings.TrimPrefix(backupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
fileItem = itemPath + strings.TrimPrefix(file, localDir+"/")
} else {
fileItem = strings.TrimPrefix(file, localDir+"/")
}
@@ -328,7 +332,9 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
}()
}
if len(backup.BackupPath) != 0 {
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -469,7 +475,9 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAcc
}()
}
if len(backup.BackupPath) != 0 {
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -543,7 +551,9 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
}()
}
if len(backup.BackupPath) != 0 {
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -584,7 +594,7 @@ func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.T
}
func (u *CronjobService) handleSystemClean() (string, error) {
return NewIDeviceService().CleanForCronjob()
return NewISettingService().SystemCleanForCronjob()
}
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
-6
View File
@@ -125,12 +125,6 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
if ioOption == "all" {
+4 -27
View File
@@ -84,13 +84,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
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,
Timeout: 6,
}); err != nil {
return false
}
@@ -111,13 +105,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
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,
Timeout: 6,
}); err != nil {
return err
}
@@ -184,14 +172,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
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,
Timeout: 300,
}); err != nil {
return err
}
@@ -208,10 +189,6 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["port"] = req.Port
upMap["username"] = req.Username
upMap["password"] = pass
upMap["ssl"] = req.SSL
upMap["client_key"] = req.ClientKey
upMap["client_cert"] = req.ClientCert
upMap["root_cert"] = req.RootCert
upMap["skip_verify"] = req.SkipVerify
upMap["description"] = req.Description
return databaseRepo.Update(req.ID, upMap)
}
-5
View File
@@ -641,11 +641,6 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
dbInfo.SSL = databaseItem.SSL
dbInfo.ClientKey = databaseItem.ClientKey
dbInfo.ClientCert = databaseItem.ClientCert
dbInfo.RootCert = databaseItem.RootCert
dbInfo.SkipVerify = databaseItem.SkipVerify
version = databaseItem.Version
} else {
-431
View File
@@ -1,431 +0,0 @@
package service
import (
"bufio"
"errors"
"fmt"
"net"
"os"
"path"
"strconv"
"strings"
"time"
"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/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/shirou/gopsutil/v3/mem"
)
const defaultDNSPath = "/etc/resolv.conf"
const defaultHostPath = "/etc/hosts"
const defaultFstab = "/etc/fstab"
type DeviceService struct{}
type IDeviceService interface {
LoadBaseInfo() (dto.DeviceBaseInfo, error)
Update(key, value string) error
UpdateHosts(req []dto.HostHelper) error
UpdatePasswd(req dto.ChangePasswd) error
UpdateSwap(req dto.SwapHelper) error
UpdateByConf(req dto.UpdateByNameAndFile) error
LoadTimeZone() ([]string, error)
CheckDNS(key, value string) (bool, error)
LoadConf(name string) (string, error)
Scan() dto.CleanData
Clean(req []dto.Clean)
CleanForCronjob() (string, error)
}
func NewIDeviceService() IDeviceService {
return &DeviceService{}
}
func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
var baseInfo dto.DeviceBaseInfo
baseInfo.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700")
baseInfo.TimeZone = common.LoadTimeZoneByCmd()
baseInfo.DNS = loadDNS()
baseInfo.Hosts = loadHosts()
baseInfo.Hostname = loadHostname()
baseInfo.User = loadUser()
ntp, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err == nil {
baseInfo.Ntp = ntp.Value
}
swapInfo, err := mem.SwapMemory()
if err != nil {
return baseInfo, err
}
baseInfo.SwapMemoryTotal = swapInfo.Total
baseInfo.SwapMemoryAvailable = swapInfo.Free
baseInfo.SwapMemoryUsed = swapInfo.Used
if baseInfo.SwapMemoryTotal != 0 {
baseInfo.SwapDetails = loadSwap()
}
disks := loadDiskInfo()
for _, item := range disks {
baseInfo.MaxSize += item.Free
}
return baseInfo, nil
}
func (u *DeviceService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, err
}
return strings.Split(std, "\n"), nil
}
func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
content, err := os.ReadFile(defaultDNSPath)
if err != nil {
return false, err
}
defer func() { _ = u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: string(content)}) }()
if key == "form" {
if err := u.Update("DNS", value); err != nil {
return false, err
}
} else {
if err := u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: value}); err != nil {
return false, err
}
}
conn, err := net.DialTimeout("ip4:icmp", "www.baidu.com", time.Second*2)
if err != nil {
return false, err
}
defer conn.Close()
return true, nil
}
func (u *DeviceService) Update(key, value string) error {
switch key {
case "TimeZone":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
}
}()
case "DNS":
if err := updateDNS(strings.Split(value, ",")); err != nil {
return err
}
case "Hostname":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
if err != nil {
return errors.New(std)
}
case "Ntp", "LocalTime":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
ntpValue := value
if key == "LocalTime" {
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
ntpValue = ntpItem.Value
} else {
if err := settingRepo.Update("NtpSite", ntpValue); err != nil {
return err
}
}
ntime, err := ntp.GetRemoteTime(ntpValue)
if err != nil {
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts); err != nil {
return err
}
default:
return fmt.Errorf("not support such key %s", key)
}
return nil
}
func (u *DeviceService) UpdateHosts(req []dto.HostHelper) error {
conf, err := os.ReadFile(defaultHostPath)
if err != nil {
return fmt.Errorf("read namesever conf of %s failed, err: %v", defaultHostPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 {
newFile += line + "\n"
continue
}
for index, item := range req {
if item.IP == parts[0] && item.Host == strings.Join(parts[1:], " ") {
newFile += line + "\n"
req = append(req[:index], req[index+1:]...)
break
}
}
}
for _, item := range req {
newFile += fmt.Sprintf("%s %s \n", item.IP, item.Host)
}
file, err := os.OpenFile(defaultHostPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
if cmd.CheckIllegal(req.User, req.Passwd) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
if err != nil {
if strings.Contains(err.Error(), "does not exist") {
return buserr.New(constant.ErrNotExistUser)
}
return errors.New(std)
}
return nil
}
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
if cmd.CheckIllegal(req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
if !req.IsNew {
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.CONF.System.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.CONF.System.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
std1, err := cmd.Execf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
}
std2, err := cmd.Execf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
}
std3, err := cmd.Execf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
}
return operateSwapWithFile(false, req)
}
func (u *DeviceService) LoadConf(name string) (string, error) {
pathItem := ""
switch name {
case "DNS":
pathItem = defaultDNSPath
case "Hosts":
pathItem = defaultHostPath
default:
return "", fmt.Errorf("not support such name %s", name)
}
if _, err := os.Stat(pathItem); err != nil {
return "", err
}
content, err := os.ReadFile(pathItem)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *DeviceService) UpdateByConf(req dto.UpdateByNameAndFile) error {
if req.Name != "DNS" && req.Name != "Hosts" {
return fmt.Errorf("not support such name %s", req.Name)
}
path := defaultDNSPath
if req.Name == "Hosts" {
path = defaultHostPath
}
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()
return nil
}
func loadDNS() []string {
var list []string
dnsConf, err := os.ReadFile(defaultDNSPath)
if err == nil {
lines := strings.Split(string(dnsConf), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "nameserver ") {
list = append(list, strings.TrimPrefix(line, "nameserver "))
}
}
}
return list
}
func updateDNS(list []string) error {
conf, err := os.ReadFile(defaultDNSPath)
if err != nil {
return fmt.Errorf("read nameserver conf of %s failed, err: %v", defaultDNSPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 || parts[0] != "nameserver" {
newFile += line + "\n"
continue
}
itemNs := strings.Join(parts[1:], " ")
for index, item := range list {
if item == itemNs {
newFile += line + "\n"
list = append(list[:index], list[index+1:]...)
break
}
}
}
for _, item := range list {
if len(item) != 0 {
newFile += fmt.Sprintf("nameserver %s \n", item)
}
}
file, err := os.OpenFile(defaultDNSPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func loadHosts() []dto.HostHelper {
var list []dto.HostHelper
hostConf, err := os.ReadFile(defaultHostPath)
if err == nil {
lines := strings.Split(string(hostConf), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 2 {
continue
}
list = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], " ")})
}
}
return list
}
func loadHostname() string {
std, err := cmd.Exec("hostname")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadUser() string {
std, err := cmd.Exec("whoami")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadSwap() []dto.SwapHelper {
var data []dto.SwapHelper
std, err := cmd.Execf("%s swapon --summary", cmd.SudoHandleCmd())
if err != nil {
return data
}
lines := strings.Split(std, "\n")
for index, line := range lines {
if index == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
sizeItem, _ := strconv.Atoi(parts[2])
data = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})
}
return data
}
func operateSwapWithFile(delete bool, req dto.SwapHelper) error {
conf, err := os.ReadFile(defaultFstab)
if err != nil {
return fmt.Errorf("read file %s failed, err: %v", defaultFstab, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) == 6 && parts[0] == req.Path {
continue
}
newFile += line + "\n"
}
if !delete {
newFile += fmt.Sprintf("%s swap swap defaults 0 0\n", req.Path)
}
file, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
+7 -65
View File
@@ -21,7 +21,6 @@ type DockerService struct{}
type IDockerService interface {
UpdateConf(req dto.SettingUpdate) error
UpdateLogOption(req dto.LogOption) error
UpdateIpv6Option(req dto.Ipv6Option) error
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
LoadDockerStatus() string
LoadDockerConf() *dto.DaemonJsonConf
@@ -33,17 +32,13 @@ func NewIDockerService() IDockerService {
}
type daemonJsonItem struct {
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
Ipv6 bool `json:"ipv6"`
FixedCidrV6 string `json:"fixed-cidr-v6"`
Ip6Tables bool `json:"ip6tables"`
Experimental bool `json:"experimental"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
}
type logOption struct {
LogMaxSize string `json:"max-size"`
@@ -115,10 +110,6 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
break
}
}
data.Ipv6 = conf.Ipv6
data.FixedCidrV6 = conf.FixedCidrV6
data.Ip6Tables = conf.Ip6Tables
data.Experimental = conf.Experimental
data.LogMaxSize = conf.LogOption.LogMaxSize
data.LogMaxFile = conf.LogOption.LogMaxFile
data.Mirrors = conf.Mirrors
@@ -158,13 +149,6 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
} else {
daemonMap["registry-mirrors"] = strings.Split(req.Value, ",")
}
case "Ipv6":
if req.Value == "disable" {
delete(daemonMap, "ipv6")
delete(daemonMap, "fixed-cidr-v6")
delete(daemonMap, "ip6tables")
delete(daemonMap, "experimental")
}
case "LogOption":
if req.Value == "disable" {
delete(daemonMap, "log-opts")
@@ -253,48 +237,6 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
return nil
}
func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
return err
}
_, _ = os.Create(constant.DaemonJsonPath)
}
file, err := os.ReadFile(constant.DaemonJsonPath)
if err != nil {
return err
}
daemonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &daemonMap)
daemonMap["ipv6"] = true
daemonMap["fixed-cidr-v6"] = req.FixedCidrV6
if req.Ip6Tables {
daemonMap["ip6tables"] = req.Ip6Tables
}
if req.Experimental {
daemonMap["experimental"] = req.Experimental
}
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil {
return err
}
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
return err
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
}
return nil
}
func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
if len(req.File) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
-1
View File
@@ -32,7 +32,6 @@ var (
websiteDnsRepo = repo.NewIWebsiteDnsAccountRepo()
websiteSSLRepo = repo.NewISSLRepo()
websiteAcmeRepo = repo.NewIAcmeAccountRepo()
websiteCARepo = repo.NewIWebsiteCARepo()
logRepo = repo.NewILogRepo()
snapshotRepo = repo.NewISnapshotRepo()
-251
View File
@@ -1,251 +0,0 @@
package service
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/firewall"
"github.com/1Panel-dev/1Panel/backend/utils/toolbox"
)
const defaultFail2BanPath = "/etc/fail2ban/jail.local"
type Fail2BanService struct{}
type IFail2BanService interface {
LoadBaseInfo() (dto.Fail2BanBaseInfo, error)
Search(search dto.Fail2BanSearch) ([]string, error)
Operate(operation string) error
OperateSSHD(req dto.Fail2BanSet) error
UpdateConf(req dto.Fail2BanUpdate) error
UpdateConfByFile(req dto.UpdateByFile) error
}
func NewIFail2BanService() IFail2BanService {
return &Fail2BanService{}
}
func (u *Fail2BanService) LoadBaseInfo() (dto.Fail2BanBaseInfo, error) {
var baseInfo dto.Fail2BanBaseInfo
client, err := toolbox.NewFail2Ban()
if err != nil {
return baseInfo, err
}
baseInfo.IsEnable, baseInfo.IsActive, baseInfo.IsExist = client.Status()
if !baseInfo.IsActive {
baseInfo.Version = "-"
} else {
baseInfo.Version = client.Version()
}
conf, err := os.ReadFile(defaultFail2BanPath)
if err != nil {
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")
block := ""
for _, line := range lines {
if strings.HasPrefix(strings.ToLower(line), "[default]") {
block = "default"
continue
}
if strings.HasPrefix(line, "[sshd]") {
block = "sshd"
continue
}
if strings.HasPrefix(line, "[") {
block = ""
continue
}
if block != "default" && block != "sshd" {
continue
}
loadFailValue(line, &baseInfo)
}
return baseInfo, nil
}
func (u *Fail2BanService) Search(req dto.Fail2BanSearch) ([]string, error) {
var list []string
client, err := toolbox.NewFail2Ban()
if err != nil {
return nil, err
}
if req.Status == "banned" {
list, err = client.ListBanned()
} else {
list, err = client.ListIgnore()
}
if err != nil {
return nil, err
}
return list, nil
}
func (u *Fail2BanService) Operate(operation string) error {
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
return client.Operate(operation)
}
func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {
if req.Key == "banaction" {
if req.Value == "firewallcmd-ipset" || req.Value == "ufw" {
itemName := "ufw"
if req.Value == "firewallcmd-ipset" {
itemName = "firewallcmd"
}
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
if client.Name() != itemName {
return buserr.WithName("ErrBanAction", itemName)
}
status, _ := client.Status()
if status != "running" {
return buserr.WithName("ErrBanAction", itemName)
}
}
}
if req.Key == "logpath" {
if _, err := os.Stat(req.Value); err != nil {
return err
}
}
conf, err := os.ReadFile(defaultFail2BanPath)
if err != nil {
return fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
}
lines := strings.Split(string(conf), "\n")
isStart, isEnd, hasKey := false, false, false
newFile := ""
for index, line := range lines {
if !isStart && strings.HasPrefix(line, "[sshd]") {
isStart = true
newFile += fmt.Sprintf("%s\n", line)
continue
}
if !isStart || isEnd {
newFile += fmt.Sprintf("%s\n", line)
continue
}
if strings.HasPrefix(line, req.Key) {
hasKey = true
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value)
continue
}
if strings.HasPrefix(line, "[") || index == len(lines)-1 {
isEnd = true
if !hasKey {
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value)
}
}
newFile += line
if index != len(lines)-1 {
newFile += "\n"
}
}
file, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.Operate("restart"); err != nil {
return err
}
return nil
}
func (u *Fail2BanService) UpdateConfByFile(req dto.UpdateByFile) error {
file, err := os.OpenFile(defaultFail2BanPath, 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()
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.Operate("restart"); err != nil {
return err
}
return nil
}
func (u *Fail2BanService) OperateSSHD(req dto.Fail2BanSet) error {
if req.Operate == "ignore" {
if err := u.UpdateConf(dto.Fail2BanUpdate{Key: "ignoreip", Value: strings.Join(req.IPs, ",")}); err != nil {
return err
}
return nil
}
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.ReBanIPs(req.IPs); err != nil {
return err
}
return nil
}
func loadFailValue(line string, baseInfo *dto.Fail2BanBaseInfo) {
if strings.HasPrefix(line, "port") {
itemValue := strings.ReplaceAll(line, "port", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.Port, _ = strconv.Atoi(strings.TrimSpace(itemValue))
}
if strings.HasPrefix(line, "maxretry") {
itemValue := strings.ReplaceAll(line, "maxretry", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.MaxRetry, _ = strconv.Atoi(strings.TrimSpace(itemValue))
}
if strings.HasPrefix(line, "findtime") {
itemValue := strings.ReplaceAll(line, "findtime", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.FindTime = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "bantime") {
itemValue := strings.ReplaceAll(line, "bantime", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.BanTime = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "banaction") {
itemValue := strings.ReplaceAll(line, "banaction", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.BanAction = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "logpath") {
itemValue := strings.ReplaceAll(line, "logpath", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.LogPath = strings.TrimSpace(itemValue)
}
}
+3 -71
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -42,7 +41,6 @@ type IFileService interface {
ChangeOwner(req request.FileRoleUpdate) error
ChangeMode(op request.FileCreate) error
BatchChangeModeAndOwner(op request.FileRoleReq) error
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
}
func NewIFileService() IFileService {
@@ -136,10 +134,6 @@ func (f *FileService) Create(op request.FileCreate) error {
func (f *FileService) Delete(op request.FileDelete) error {
fo := files.NewFileOp()
recycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey("FileRecycleBin"))
if recycleBinStatus.Value == "disable" {
op.ForceDelete = true
}
if op.ForceDelete {
if op.IsDir {
return fo.DeleteDir(op.Path)
@@ -243,7 +237,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, w.IgnoreCertificate)
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
}
func (f *FileService) MvFile(m request.FileMove) error {
@@ -251,11 +245,8 @@ func (f *FileService) MvFile(m request.FileMove) error {
if !fo.Stat(m.NewPath) {
return buserr.New(constant.ErrPathNotFound)
}
for _, oldPath := range m.OldPaths {
if !fo.Stat(oldPath) {
return buserr.WithName(constant.ErrFileNotFound, oldPath)
}
if oldPath == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(oldPath)+"/") {
for _, path := range m.OldPaths {
if path == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(path)+"/") {
return buserr.New(constant.ErrMovePathFailed)
}
}
@@ -306,62 +297,3 @@ func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, erro
}
return response.DirSizeRes{Size: size}, nil
}
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := ""
switch req.Type {
case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
nginx, err := getNginxFull(&website)
if err != nil {
return nil, err
}
sitePath := path.Join(nginx.SiteDir, "sites", website.Alias)
logFilePath = path.Join(sitePath, "log", req.Name)
case constant.TypePhp:
php, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = php.GetLogPath()
case constant.TypeSSL:
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = ssl.GetLogPath()
case constant.TypeSystem:
fileName := ""
if len(req.Name) == 0 || req.Name == time.Now().Format("2006-01-02") {
fileName = "1Panel.log"
} else {
fileName = "1Panel-" + req.Name + ".log"
}
logFilePath = path.Join(global.CONF.System.DataDir, "log", fileName)
if _, err := os.Stat(logFilePath); err != nil {
fileGzPath := path.Join(global.CONF.System.DataDir, "log", fileName+".gz")
if _, err := os.Stat(fileGzPath); err != nil {
return nil, buserr.New("ErrHttpReqNotFound")
}
if err := handleGunzip(fileGzPath); err != nil {
return nil, fmt.Errorf("handle ungzip file %s failed, err: %v", fileGzPath, err)
}
}
case "image-pull", "image-push", "image-build", "compose-create":
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
}
lines, isEndOfFile, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize)
if err != nil {
return nil, err
}
res := &response.FileLineContent{
Content: strings.Join(lines, "\n"),
End: isEndOfFile,
Path: logFilePath,
}
return res, nil
}
+2 -2
View File
@@ -390,8 +390,8 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
return err
}
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)
}
+1 -1
View File
@@ -51,7 +51,7 @@ func (u *LogService) ListSystemLogFile() ([]string, error) {
if err != nil {
return err
}
if !info.IsDir() && strings.HasPrefix(info.Name(), "1Panel") {
if !info.IsDir() {
if info.Name() == "1Panel.log" {
files = append(files, time.Now().Format("2006-01-02"))
return nil
+3 -3
View File
@@ -104,7 +104,7 @@ func (r RecycleBinService) Create(create request.RecycleBinCreate) error {
}
rName := fmt.Sprintf("_1p_%s%s_p_%d_%d", "file", rNamePre, size, deleteTime.Unix())
return op.Mv(create.SourcePath, path.Join(clashDir, rName))
return op.Rename(create.SourcePath, path.Join(clashDir, rName))
}
func (r RecycleBinService) Reduce(reduce request.RecycleBinReduce) error {
@@ -125,7 +125,7 @@ func (r RecycleBinService) Reduce(reduce request.RecycleBinReduce) error {
return err
}
}
return op.Mv(filePath, recycleBinDTO.SourcePath)
return op.Rename(filePath, recycleBinDTO.SourcePath)
}
func (r RecycleBinService) Clear() error {
@@ -140,7 +140,7 @@ func (r RecycleBinService) Clear() error {
continue
}
newDir := path.Join(p.Mountpoint, "1panel_clash")
if err := op.Mv(dir, newDir); err != nil {
if err := op.Rename(dir, newDir); err != nil {
return err
}
go func() {
+19 -24
View File
@@ -21,7 +21,6 @@ import (
"github.com/subosito/gotenv"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -33,7 +32,7 @@ type RuntimeService struct {
type IRuntimeService interface {
Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error)
Create(create request.RuntimeCreate) (*model.Runtime, error)
Create(create request.RuntimeCreate) error
Delete(delete request.RuntimeDelete) error
Update(req request.RuntimeUpdate) error
Get(id uint) (res *response.RuntimeDTO, err error)
@@ -48,7 +47,7 @@ func NewRuntimeService() IRuntimeService {
return &RuntimeService{}
}
func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, error) {
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
var (
opts []repo.DBOption
)
@@ -60,7 +59,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
}
exist, _ := runtimeRepo.GetFirst(opts...)
if exist != nil {
return nil, buserr.New(constant.ErrNameIsExist)
return buserr.New(constant.ErrNameIsExist)
}
fileOp := files.NewFileOp()
@@ -74,45 +73,44 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
Version: create.Version,
Status: constant.RuntimeNormal,
}
return nil, runtimeRepo.Create(context.Background(), runtime)
return runtimeRepo.Create(context.Background(), runtime)
}
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
if exist != nil {
return nil, buserr.New(constant.ErrImageExist)
return buserr.New(constant.ErrImageExist)
}
case constant.RuntimeNode:
if !fileOp.Stat(create.CodeDir) {
return nil, buserr.New(constant.ErrPathNotFound)
return buserr.New(constant.ErrPathNotFound)
}
create.Install = true
if err := checkPortExist(create.Port); err != nil {
return nil, err
if err = checkPortExist(create.Port); err != nil {
return err
}
for _, export := range create.ExposedPorts {
if err := checkPortExist(export.HostPort); err != nil {
return nil, err
if err = checkPortExist(export.HostPort); err != nil {
return err
}
}
if containerName, ok := create.Params["CONTAINER_NAME"]; ok {
if err := checkContainerName(containerName.(string)); err != nil {
return nil, err
return err
}
}
}
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
if err != nil {
return nil, err
return err
}
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil {
return nil, err
return err
}
appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version)
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
if !fileOp.Stat(appVersionDir) || appDetail.Update {
if err = downloadApp(app, appDetail, nil); err != nil {
return nil, err
if err := downloadApp(app, appDetail, nil); err != nil {
return err
}
}
@@ -128,18 +126,15 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
switch create.Type {
case constant.RuntimePHP:
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
return nil, err
return
}
case constant.RuntimeNode:
runtime.Port = create.Port
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
return nil, err
return
}
}
if err := runtimeRepo.Create(context.Background(), runtime); err != nil {
return nil, err
}
return runtime, nil
return runtimeRepo.Create(context.Background(), runtime)
}
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error) {
+68 -56
View File
@@ -13,8 +13,6 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -23,6 +21,8 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
)
@@ -32,6 +32,7 @@ type SettingService struct{}
type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error)
LoadInterfaceAddr() ([]string, error)
LoadTimeZone() ([]string, error)
Update(key, value string) error
UpdatePassword(c *gin.Context, old, new string) error
UpdatePort(port uint) error
@@ -39,6 +40,11 @@ type ISettingService interface {
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error
SyncTime(req dto.SyncTime) error
SystemScan() dto.CleanData
SystemClean(req []dto.Clean)
SystemCleanForCronjob() (string, error)
}
func NewISettingService() ISettingService {
@@ -66,6 +72,14 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
return &info, err
}
func (u *SettingService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, nil
}
return strings.Split(std, "\n"), err
}
func (u *SettingService) Update(key, value string) error {
switch key {
case "MonitorStatus":
@@ -93,6 +107,10 @@ func (u *SettingService) Update(key, value string) error {
return err
}
}
case "TimeZone":
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
case "AppStoreLastModified":
exist, _ := settingRepo.Get(settingRepo.WithByKey("AppStoreLastModified"))
if exist.ID == 0 {
@@ -107,13 +125,17 @@ func (u *SettingService) Update(key, value string) error {
switch key {
case "ExpirationDays":
timeout, err := strconv.Atoi(value)
if err != nil {
return err
}
timeout, _ := strconv.Atoi(value)
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
return err
}
case "TimeZone":
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
}
}()
case "BindDomain":
if len(value) != 0 {
_ = global.SESSION.Clean()
@@ -126,6 +148,21 @@ func (u *SettingService) Update(key, value string) error {
return nil
}
func (u *SettingService) SyncTime(req dto.SyncTime) error {
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
return err
}
ntime, err := ntp.GetRemoteTime(req.NtpSite)
if err != nil {
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts); err != nil {
return err
}
return nil
}
func (u *SettingService) LoadInterfaceAddr() ([]string, error) {
addrMap := make(map[string]struct{})
addrs, err := net.InterfaceAddrs()
@@ -153,7 +190,6 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
return err
}
go func() {
time.Sleep(1 * time.Second)
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system with new bind info failed, err: %v", err)
@@ -178,7 +214,6 @@ func (u *SettingService) UpdatePort(port uint) error {
return err
}
go func() {
time.Sleep(1 * time.Second)
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system port failed, err: %v", err)
@@ -206,6 +241,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
}()
return nil
}
if _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(secretDir, os.ModePerm); err != nil {
return err
@@ -214,72 +250,49 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
if err := settingRepo.Update("SSLType", req.SSLType); err != nil {
return err
}
var (
secret string
key string
)
switch req.SSLType {
case "self":
if req.SSLType == "self" {
if len(req.Domain) == 0 {
return fmt.Errorf("load domain failed")
}
defaultCA, err := websiteCARepo.GetFirst(commonRepo.WithByName("1Panel"))
if err := ssl.GenerateSSL(req.Domain); err != nil {
return err
}
}
if req.SSLType == "select" {
sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
websiteSSL, err := NewIWebsiteCAService().ObtainSSL(request.WebsiteCAObtain{
ID: defaultCA.ID,
KeyType: "P256",
Domains: req.Domain,
Time: 1,
Unit: "year",
AutoRenew: true,
})
if err != nil {
return err
}
secret = websiteSSL.Pem
key = websiteSSL.PrivateKey
if err := settingRepo.Update("SSLID", strconv.Itoa(int(websiteSSL.ID))); err != nil {
return err
}
case "select":
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
secret = websiteSSL.Pem
key = websiteSSL.PrivateKey
req.Cert = sslInfo.Pem
req.Key = sslInfo.PrivateKey
req.SSLType = "import"
if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil {
return err
}
case "import-paste":
secret = req.Cert
key = req.Key
case "import-local":
keyFile, err := os.ReadFile(req.Key)
}
if req.SSLType == "import" {
cert, err := os.OpenFile(path.Join(secretDir, "server.crt.tmp"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
key = string(keyFile)
certFile, err := os.ReadFile(req.Cert)
defer cert.Close()
if _, err := cert.WriteString(req.Cert); err != nil {
return err
}
key, err := os.OpenFile(path.Join(secretDir, "server.key.tmp"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
secret = string(certFile)
}
fileOp := files.NewFileOp()
if err := fileOp.WriteFile(path.Join(secretDir, "server.crt.tmp"), strings.NewReader(secret), 0600); err != nil {
return err
}
if err := fileOp.WriteFile(path.Join(secretDir, "server.key.tmp"), strings.NewReader(key), 0600); err != nil {
return err
if _, err := key.WriteString(req.Key); err != nil {
return err
}
defer key.Close()
}
if err := checkCertValid(req.Domain); err != nil {
return err
}
fileOp := files.NewFileOp()
if err := fileOp.Rename(path.Join(secretDir, "server.crt.tmp"), path.Join(secretDir, "server.crt")); err != nil {
return err
}
@@ -290,7 +303,6 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
return err
}
go func() {
time.Sleep(1 * time.Second)
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system failed, err: %v", err)
@@ -33,7 +33,7 @@ const (
taskPath = "1panel/task"
)
func (u *DeviceService) Scan() dto.CleanData {
func (u *SettingService) SystemScan() dto.CleanData {
var (
SystemClean dto.CleanData
treeData []dto.CleanTree
@@ -133,7 +133,7 @@ func (u *DeviceService) Scan() dto.CleanData {
return SystemClean
}
func (u *DeviceService) Clean(req []dto.Clean) {
func (u *SettingService) SystemClean(req []dto.Clean) {
size := uint64(0)
restart := false
for _, item := range req {
@@ -235,7 +235,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
continue
}
for _, file := range files {
if file.Name() == "1Panel.log" || file.IsDir() {
if file.Name() == "1Panel.log" {
continue
}
dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, file.Name()))
@@ -276,7 +276,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
}
}
func (u *DeviceService) CleanForCronjob() (string, error) {
func (u *SettingService) SystemCleanForCronjob() (string, error) {
logs := ""
size := int64(0)
fileCount := 0
@@ -541,7 +541,7 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
return lists
}
for _, file := range files {
if treeType == "system_log" && (file.Name() == "1Panel.log" || file.IsDir()) {
if treeType == "system_log" && file.Name() == "1Panel.log" {
continue
}
if (treeType == "upload" || treeType == "download") && file.IsDir() && (file.Name() == "app" || file.Name() == "database" || file.Name() == "website" || file.Name() == "directory") {
+1 -1
View File
@@ -320,7 +320,7 @@ 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 -aE 'Failed password for|Connection closed by authenticating user' | grep -v 'invalid' %s", file.Name, command)
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' | grep -a 'preauth' %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)
}
+9 -8
View File
@@ -173,6 +173,9 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
primaryDomainArray := strings.Split(create.PrimaryDomain, ":")
primaryDomain := primaryDomainArray[0]
if exist, _ := websiteRepo.GetBy(websiteRepo.WithDomain(primaryDomain)); len(exist) > 0 {
return buserr.New(constant.ErrDomainIsExist)
}
if exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(create.Alias)); len(exist) > 0 {
return buserr.New(constant.ErrAliasIsExist)
}
@@ -616,7 +619,7 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
if err != nil {
return response.WebsiteHTTPS{}, err
}
res.SSL = *websiteSSL
res.SSL = websiteSSL
res.Enable = true
if website.HttpConfig != "" {
res.HttpConfig = website.HttpConfig
@@ -690,13 +693,12 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
}
if req.Type == constant.SSLExisted {
websiteModel, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
if err != nil {
return nil, err
}
website.WebsiteSSLID = websiteModel.ID
res.SSL = *websiteModel
websiteSSL = *websiteModel
website.WebsiteSSLID = websiteSSL.ID
res.SSL = websiteSSL
}
if req.Type == constant.SSLManual {
var (
@@ -1210,7 +1212,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
if err != nil {
return err
}
oldRuntime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
oldRuntime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.RuntimeID))
if err != nil {
return err
}
@@ -1268,7 +1270,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
return busErr
}
newComposeByte, busErr = changeServiceName(appDetail.DockerCompose, appInstall.ServiceName)
newComposeByte, busErr = changeServiceName(composePath, appInstall.ServiceName)
if busErr != nil {
return err
}
@@ -1289,7 +1291,6 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
return busErr
}
}
if out, err := compose.Up(appInstall.GetComposePath()); err != nil {
if out != "" {
busErr = errors.New(out)
+12 -27
View File
@@ -15,7 +15,7 @@ type WebsiteAcmeAccountService struct {
type IWebsiteAcmeAccountService interface {
Page(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error)
Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error)
Create(create request.WebsiteAcmeAccountCreate) (response.WebsiteAcmeAccountDTO, error)
Delete(id uint) error
}
@@ -34,40 +34,25 @@ func (w WebsiteAcmeAccountService) Page(search dto.PageInfo) (int64, []response.
return total, accountDTOs, err
}
func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error) {
func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (response.WebsiteAcmeAccountDTO, error) {
exist, _ := websiteAcmeRepo.GetFirst(websiteAcmeRepo.WithEmail(create.Email))
if exist != nil {
return nil, buserr.New(constant.ErrEmailIsExist)
}
acmeAccount := &model.WebsiteAcmeAccount{
Email: create.Email,
Type: create.Type,
KeyType: create.KeyType,
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
}
if create.Type == "google" {
if create.EabKid == "" || create.EabHmacKey == "" {
return nil, buserr.New(constant.ErrEabKidOrEabHmacKeyCannotBlank)
}
acmeAccount.EabKid = create.EabKid
acmeAccount.EabHmacKey = create.EabHmacKey
}
client, err := ssl.NewAcmeClient(acmeAccount)
client, err := ssl.NewAcmeClient(create.Email, "")
if err != nil {
return nil, err
return response.WebsiteAcmeAccountDTO{}, err
}
privateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))
if err != nil {
return nil, err
acmeAccount := model.WebsiteAcmeAccount{
Email: create.Email,
URL: client.User.Registration.URI,
PrivateKey: string(ssl.GetPrivateKey(client.User.GetPrivateKey())),
}
acmeAccount.PrivateKey = string(privateKey)
acmeAccount.URL = client.User.Registration.URI
if err := websiteAcmeRepo.Create(*acmeAccount); err != nil {
return nil, err
if err := websiteAcmeRepo.Create(acmeAccount); err != nil {
return response.WebsiteAcmeAccountDTO{}, err
}
return &response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: acmeAccount}, nil
}
func (w WebsiteAcmeAccountService) Delete(id uint) error {
-388
View File
@@ -1,388 +0,0 @@
package service
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"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"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
"github.com/go-acme/lego/v4/certcrypto"
"log"
"math/big"
"net"
"os"
"path"
"strings"
"time"
)
type WebsiteCAService struct {
}
type IWebsiteCAService interface {
Page(search request.WebsiteCASearch) (int64, []response.WebsiteCADTO, error)
Create(create request.WebsiteCACreate) (*request.WebsiteCACreate, error)
GetCA(id uint) (*response.WebsiteCADTO, error)
Delete(id uint) error
ObtainSSL(req request.WebsiteCAObtain) (*model.WebsiteSSL, error)
}
func NewIWebsiteCAService() IWebsiteCAService {
return &WebsiteCAService{}
}
func (w WebsiteCAService) Page(search request.WebsiteCASearch) (int64, []response.WebsiteCADTO, error) {
total, cas, err := websiteCARepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
if err != nil {
return 0, nil, err
}
var caDTOs []response.WebsiteCADTO
for _, ca := range cas {
caDTOs = append(caDTOs, response.WebsiteCADTO{
WebsiteCA: ca,
})
}
return total, caDTOs, err
}
func (w WebsiteCAService) Create(create request.WebsiteCACreate) (*request.WebsiteCACreate, error) {
if exist, _ := websiteCARepo.GetFirst(commonRepo.WithByName(create.Name)); exist.ID > 0 {
return nil, buserr.New(constant.ErrNameIsExist)
}
ca := &model.WebsiteCA{
Name: create.Name,
KeyType: create.KeyType,
}
pkixName := pkix.Name{
CommonName: create.CommonName,
Country: []string{create.Country},
Organization: []string{create.Organization},
OrganizationalUnit: []string{create.OrganizationUint},
}
if create.Province != "" {
pkixName.Province = []string{create.Province}
}
if create.City != "" {
pkixName.Locality = []string{create.City}
}
rootCA := &x509.Certificate{
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: pkixName,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 1,
MaxPathLenZero: false,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
}
var (
caPEM = new(bytes.Buffer)
)
interPrivateKey, interPublicKey, privateBytes, err := createPrivateKey(create.KeyType)
if err != nil {
return nil, err
}
ca.PrivateKey = string(privateBytes)
caBytes, err := x509.CreateCertificate(rand.Reader, rootCA, rootCA, interPublicKey, interPrivateKey)
if err != nil {
return nil, err
}
certBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
_ = pem.Encode(caPEM, certBlock)
pemData := pem.EncodeToMemory(certBlock)
ca.CSR = string(pemData)
if err := websiteCARepo.Create(context.Background(), ca); err != nil {
return nil, err
}
return &create, nil
}
func (w WebsiteCAService) GetCA(id uint) (*response.WebsiteCADTO, error) {
res := &response.WebsiteCADTO{}
ca, err := websiteCARepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return nil, err
}
res.WebsiteCA = ca
certBlock, _ := pem.Decode([]byte(ca.CSR))
if certBlock == nil {
return nil, buserr.New("ErrSSLCertificateFormat")
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, err
}
res.CommonName = cert.Issuer.CommonName
res.Organization = strings.Join(cert.Issuer.Organization, ",")
res.Country = strings.Join(cert.Issuer.Country, ",")
res.Province = strings.Join(cert.Issuer.Province, ",")
res.City = strings.Join(cert.Issuer.Locality, ",")
res.OrganizationUint = strings.Join(cert.Issuer.OrganizationalUnit, ",")
return res, nil
}
func (w WebsiteCAService) Delete(id uint) error {
ssls, _ := websiteSSLRepo.List(websiteSSLRepo.WithByCAID(id))
if len(ssls) > 0 {
return buserr.New("ErrDeleteCAWithSSL")
}
exist, err := websiteCARepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return err
}
if exist.Name == "1Panel" {
return buserr.New("ErrDefaultCA")
}
return websiteCARepo.DeleteBy(commonRepo.WithByID(id))
}
func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.WebsiteSSL, error) {
var (
domains []string
ips []net.IP
websiteSSL = &model.WebsiteSSL{}
err error
ca model.WebsiteCA
)
if req.Renew {
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return nil, err
}
ca, err = websiteCARepo.GetFirst(commonRepo.WithByID(websiteSSL.CaID))
if err != nil {
return nil, err
}
existDomains := []string{websiteSSL.PrimaryDomain}
if websiteSSL.Domains != "" {
existDomains = append(existDomains, strings.Split(websiteSSL.Domains, ",")...)
}
for _, domain := range existDomains {
if ipAddress := net.ParseIP(domain); ipAddress == nil {
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
}
}
} else {
ca, err = websiteCARepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
websiteSSL = &model.WebsiteSSL{
Provider: constant.SelfSigned,
KeyType: req.KeyType,
PushDir: req.PushDir,
CaID: ca.ID,
AutoRenew: req.AutoRenew,
Description: req.Description,
}
if req.PushDir {
if !files.NewFileOp().Stat(req.Dir) {
return nil, buserr.New(constant.ErrLinkPathNotFound)
}
websiteSSL.Dir = req.Dir
}
if req.Domains != "" {
domainArray := strings.Split(req.Domains, "\n")
for _, domain := range domainArray {
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return nil, err
} else {
if ipAddress := net.ParseIP(domain); ipAddress == nil {
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
}
}
}
if len(domains) > 0 {
websiteSSL.PrimaryDomain = domains[0]
websiteSSL.Domains = strings.Join(domains[1:], ",")
}
ipStrings := make([]string, len(ips))
for i, ip := range ips {
ipStrings[i] = ip.String()
}
if websiteSSL.PrimaryDomain == "" && len(ips) > 0 {
websiteSSL.PrimaryDomain = ipStrings[0]
ipStrings = ipStrings[1:]
}
if len(ipStrings) > 0 {
if websiteSSL.Domains != "" {
websiteSSL.Domains += ","
}
websiteSSL.Domains += strings.Join(ipStrings, ",")
}
}
}
rootCertBlock, _ := pem.Decode([]byte(ca.CSR))
if rootCertBlock == nil {
return nil, buserr.New("ErrSSLCertificateFormat")
}
rootCsr, err := x509.ParseCertificate(rootCertBlock.Bytes)
if err != nil {
return nil, err
}
rootPrivateKeyBlock, _ := pem.Decode([]byte(ca.PrivateKey))
if rootPrivateKeyBlock == nil {
return nil, buserr.New("ErrSSLCertificateFormat")
}
var rootPrivateKey any
if ssl.KeyType(ca.KeyType) == certcrypto.EC256 || ssl.KeyType(ca.KeyType) == certcrypto.EC384 {
rootPrivateKey, err = x509.ParseECPrivateKey(rootPrivateKeyBlock.Bytes)
if err != nil {
return nil, err
}
} else {
rootPrivateKey, err = x509.ParsePKCS1PrivateKey(rootPrivateKeyBlock.Bytes)
if err != nil {
return nil, err
}
}
interPrivateKey, interPublicKey, _, err := createPrivateKey(websiteSSL.KeyType)
if err != nil {
return nil, err
}
notAfter := time.Now()
if req.Unit == "year" {
notAfter = notAfter.AddDate(req.Time, 0, 0)
} else {
notAfter = notAfter.AddDate(0, 0, req.Time)
}
interCsr := &x509.Certificate{
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: rootCsr.Subject,
NotBefore: time.Now(),
NotAfter: notAfter,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 0,
MaxPathLenZero: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
}
interDer, err := x509.CreateCertificate(rand.Reader, interCsr, rootCsr, interPublicKey, rootPrivateKey)
if err != nil {
return nil, err
}
interCert, err := x509.ParseCertificate(interDer)
if err != nil {
return nil, err
}
_, publicKey, privateKeyBytes, err := createPrivateKey(websiteSSL.KeyType)
if err != nil {
return nil, err
}
csr := &x509.Certificate{
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: rootCsr.Subject,
NotBefore: time.Now(),
NotAfter: notAfter,
BasicConstraintsValid: true,
IsCA: false,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
DNSNames: domains,
IPAddresses: ips,
}
der, err := x509.CreateCertificate(rand.Reader, csr, interCert, publicKey, interPrivateKey)
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, err
}
certBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}
pemData := pem.EncodeToMemory(certBlock)
websiteSSL.Pem = string(pemData)
websiteSSL.PrivateKey = string(privateKeyBytes)
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = rootCsr.Subject.Organization[0]
if req.Renew {
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
return nil, err
}
} else {
if err := websiteSSLRepo.Create(context.Background(), websiteSSL); err != nil {
return nil, err
}
}
logFile, _ := os.OpenFile(path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags)
logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
saveCertificateFile(websiteSSL, logger)
return websiteSSL, nil
}
func createPrivateKey(keyType string) (privateKey any, publicKey any, privateKeyBytes []byte, err error) {
privateKey, err = certcrypto.GeneratePrivateKey(ssl.KeyType(keyType))
if err != nil {
return
}
var (
caPrivateKeyPEM = new(bytes.Buffer)
)
if ssl.KeyType(keyType) == certcrypto.EC256 || ssl.KeyType(keyType) == certcrypto.EC384 {
publicKey = &privateKey.(*ecdsa.PrivateKey).PublicKey
publicKey = publicKey.(*ecdsa.PublicKey)
block := &pem.Block{
Type: "EC PRIVATE KEY",
}
privateBytes, sErr := x509.MarshalECPrivateKey(privateKey.(*ecdsa.PrivateKey))
if sErr != nil {
err = sErr
return
}
block.Bytes = privateBytes
_ = pem.Encode(caPrivateKeyPEM, block)
} else {
publicKey = &privateKey.(*rsa.PrivateKey).PublicKey
_ = pem.Encode(caPrivateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey.(*rsa.PrivateKey)),
})
}
privateKeyBytes = caPrivateKeyPEM.Bytes()
return
}
+119 -272
View File
@@ -4,7 +4,6 @@ import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"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"
@@ -12,19 +11,11 @@ import (
"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/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
"github.com/go-acme/lego/v4/certcrypto"
legoLogger "github.com/go-acme/lego/v4/log"
"github.com/jinzhu/gorm"
"log"
"os"
"path"
"strconv"
"strings"
"time"
)
type WebsiteSSLService struct {
@@ -35,14 +26,12 @@ type IWebsiteSSLService interface {
GetSSL(id uint) (*response.WebsiteSSLDTO, error)
Search(req request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error)
Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error)
Renew(sslId uint) error
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)
GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error)
Delete(ids []uint) error
Delete(id uint) error
Update(update request.WebsiteSSLUpdate) error
Upload(req request.WebsiteSSLUpload) error
ObtainSSL(apply request.WebsiteSSLApply) error
SyncForRestart() error
DownloadFile(id uint) (*os.File, error)
}
func NewIWebsiteSSLService() IWebsiteSSLService {
@@ -57,10 +46,9 @@ func (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []respo
if err != nil {
return 0, nil, err
}
for _, model := range sslList {
for _, sslModel := range sslList {
result = append(result, response.WebsiteSSLDTO{
WebsiteSSL: model,
LogPath: path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", model.PrimaryDomain, model.ID)),
WebsiteSSL: sslModel,
})
}
return total, result, err
@@ -72,7 +60,7 @@ func (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {
if err != nil {
return nil, err
}
res.WebsiteSSL = *websiteSSL
res.WebsiteSSL = websiteSSL
return &res, nil
}
@@ -107,203 +95,154 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
if err != nil {
return res, err
}
websiteSSL := model.WebsiteSSL{
Status: constant.SSLInit,
Provider: create.Provider,
AcmeAccountID: acmeAccount.ID,
PrimaryDomain: create.PrimaryDomain,
ExpireDate: time.Now(),
KeyType: create.KeyType,
PushDir: create.PushDir,
Description: create.Description,
}
if create.PushDir {
if !files.NewFileOp().Stat(create.Dir) {
return res, buserr.New(constant.ErrLinkPathNotFound)
}
websiteSSL.Dir = create.Dir
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
if err != nil {
return res, err
}
var domains []string
if create.OtherDomains != "" {
otherDomainArray := strings.Split(create.OtherDomains, "\n")
for _, domain := range otherDomainArray {
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return res, err
}
domains = append(domains, domain)
}
}
websiteSSL.Domains = strings.Join(domains, ",")
var websiteSSL model.WebsiteSSL
if create.Provider == constant.DNSAccount || create.Provider == constant.Http {
websiteSSL.AutoRenew = create.AutoRenew
}
if create.Provider == constant.DNSAccount {
switch create.Provider {
case constant.DNSAccount:
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(create.DnsAccountID))
if err != nil {
return res, err
}
websiteSSL.DnsAccountID = dnsAccount.ID
if err := client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
return res, err
}
websiteSSL.AutoRenew = create.AutoRenew
case constant.Http:
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return request.WebsiteSSLCreate{}, err
}
if err := client.UseHTTP(path.Join(appInstall.GetPath(), "root")); err != nil {
return res, err
}
websiteSSL.AutoRenew = create.AutoRenew
case constant.DnsManual:
if err := client.UseManualDns(); err != nil {
return res, err
}
}
domains := []string{create.PrimaryDomain}
otherDomainArray := strings.Split(create.OtherDomains, "\n")
if create.OtherDomains != "" {
domains = append(otherDomainArray, domains...)
}
resource, err := client.ObtainSSL(domains)
if err != nil {
return res, err
}
if create.Provider == constant.DNSAccount {
websiteSSL.DnsAccountID = create.DnsAccountID
}
websiteSSL.AcmeAccountID = acmeAccount.ID
websiteSSL.Provider = create.Provider
websiteSSL.Domains = strings.Join(otherDomainArray, ",")
websiteSSL.PrimaryDomain = create.PrimaryDomain
websiteSSL.PrivateKey = string(resource.PrivateKey)
websiteSSL.Pem = string(resource.Certificate)
websiteSSL.CertURL = resource.CertURL
certBlock, _ := pem.Decode(resource.Certificate)
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return request.WebsiteSSLCreate{}, err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
return res, err
}
create.ID = websiteSSL.ID
go func() {
if create.Provider != constant.DnsManual {
if err = w.ObtainSSL(request.WebsiteSSLApply{
ID: websiteSSL.ID,
}); err != nil {
global.LOG.Errorf("obtain ssl failed, err: %v", err)
}
}
}()
return create, nil
}
func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
var (
err error
websiteSSL *model.WebsiteSSL
acmeAccount *model.WebsiteAcmeAccount
dnsAccount *model.WebsiteDnsAccount
)
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(apply.ID))
func (w WebsiteSSLService) Renew(sslId uint) error {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(sslId))
if err != nil {
return err
}
acmeAccount, err = websiteAcmeRepo.GetFirst(commonRepo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
return err
}
client, err := ssl.NewAcmeClient(acmeAccount)
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
return err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
if err != nil {
return err
}
switch websiteSSL.Provider {
case constant.DNSAccount:
dnsAccount, err = websiteDnsRepo.GetFirst(commonRepo.WithByID(websiteSSL.DnsAccountID))
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(websiteSSL.DnsAccountID))
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); err != nil {
return err
}
case constant.Http:
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
if gorm.IsRecordNotFoundError(err) {
return buserr.New("ErrOpenrestyNotFound")
}
return err
}
if err := client.UseHTTP(path.Join(appInstall.GetPath(), "root")); err != nil {
return err
}
case constant.DnsManual:
if err := client.UseManualDns(); err != nil {
if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
return err
}
}
domains := []string{websiteSSL.PrimaryDomain}
if websiteSSL.Domains != "" {
domains = append(domains, strings.Split(websiteSSL.Domains, ",")...)
}
privateKey, err := certcrypto.GeneratePrivateKey(ssl.KeyType(websiteSSL.KeyType))
resource, err := client.RenewSSL(websiteSSL.CertURL)
if err != nil {
return err
}
websiteSSL.Status = constant.SSLApply
err = websiteSSLRepo.Save(websiteSSL)
websiteSSL.PrivateKey = string(resource.PrivateKey)
websiteSSL.Pem = string(resource.Certificate)
websiteSSL.CertURL = resource.CertURL
certBlock, _ := pem.Decode(resource.Certificate)
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
go func() {
logFile, _ := os.OpenFile(path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags)
legoLogger.Logger = logger
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
if websiteSSL.Provider == constant.DNSAccount {
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
}
legoLogger.Logger.Println(startMsg)
resource, err := client.ObtainSSL(domains, privateKey)
if err != nil {
handleError(websiteSSL, err)
return
}
websiteSSL.PrivateKey = string(resource.PrivateKey)
websiteSSL.Pem = string(resource.Certificate)
websiteSSL.CertURL = resource.CertURL
certBlock, _ := pem.Decode(resource.Certificate)
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
handleError(websiteSSL, err)
return
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
websiteSSL.Status = constant.SSLReady
legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
saveCertificateFile(websiteSSL, logger)
err = websiteSSLRepo.Save(websiteSSL)
if err != nil {
return
}
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
return err
}
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) > 0 {
for _, website := range websites {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain}))
if err := createPemFile(website, *websiteSSL); err != nil {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}))
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return
}
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
legoLogger.Logger.Println(i18n.GetMsgByKey(constant.ErrSSLApply))
return
}
legoLogger.Logger.Println(i18n.GetMsgByKey("ApplyWebSiteSSLSuccess"))
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(sslId))
for _, website := range websites {
if err := createPemFile(website, websiteSSL); err != nil {
global.LOG.Errorf("create website [%s] ssl file failed! err:%s", website.PrimaryDomain, err.Error())
}
}()
}
if len(websites) > 0 {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return buserr.New(constant.ErrSSLApply)
}
}
return nil
}
func handleError(websiteSSL *model.WebsiteSSL, err error) {
if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {
websiteSSL.Status = constant.Error
} else {
websiteSSL.Status = constant.SSLApplyError
}
websiteSSL.Message = err.Error()
legoLogger.Logger.Println(i18n.GetErrMsg("ApplySSLFailed", map[string]interface{}{"domain": websiteSSL.PrimaryDomain, "detail": err.Error()}))
_ = websiteSSLRepo.Save(websiteSSL)
}
func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID))
if err != nil {
return nil, err
}
client, err := ssl.NewAcmeClient(acmeAccount)
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
if err != nil {
return nil, err
}
@@ -333,34 +272,15 @@ func (w WebsiteSSLService) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO
if err != nil {
return res, err
}
res.WebsiteSSL = *websiteSSL
res.WebsiteSSL = websiteSSL
return res, nil
}
func (w WebsiteSSLService) Delete(ids []uint) error {
var names []string
for _, id := range ids {
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
oldSSL, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(id))
if oldSSL.ID > 0 {
names = append(names, oldSSL.PrimaryDomain)
}
continue
}
sslSetting, _ := settingRepo.Get(settingRepo.WithByKey("SSL"))
if sslSetting.Value == "enable" {
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
idValue, _ := strconv.Atoi(sslID.Value)
if idValue > 0 && uint(idValue) == id {
return buserr.New("ErrDeleteWithPanelSSL")
}
}
_ = websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
func (w WebsiteSSLService) Delete(id uint) error {
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
return buserr.New(constant.ErrSSLCannotDelete)
}
if len(names) > 0 {
return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ","))
}
return nil
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
}
func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
@@ -369,23 +289,14 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
return err
}
websiteSSL.AutoRenew = update.AutoRenew
websiteSSL.Description = update.Description
return websiteSSLRepo.Save(websiteSSL)
}
func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
websiteSSL := &model.WebsiteSSL{
Provider: constant.Manual,
Description: req.Description,
}
var err error
if req.SSLID > 0 {
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
websiteSSL.Description = req.Description
newSSL := &model.WebsiteSSL{
Provider: constant.Manual,
}
if req.Type == "local" {
fileOp := files.NewFileOp()
if !fileOp.Stat(req.PrivateKeyPath) {
@@ -397,24 +308,24 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {
return err
} else {
websiteSSL.PrivateKey = string(content)
newSSL.PrivateKey = string(content)
}
if content, err := fileOp.GetContent(req.CertificatePath); err != nil {
return err
} else {
websiteSSL.Pem = string(content)
newSSL.Pem = string(content)
}
} else {
websiteSSL.PrivateKey = req.PrivateKey
websiteSSL.Pem = req.Certificate
newSSL.PrivateKey = req.PrivateKey
newSSL.Pem = req.Certificate
}
privateKeyCertBlock, _ := pem.Decode([]byte(websiteSSL.PrivateKey))
privateKeyCertBlock, _ := pem.Decode([]byte(newSSL.PrivateKey))
if privateKeyCertBlock == nil {
return buserr.New("ErrSSLKeyFormat")
}
certBlock, _ := pem.Decode([]byte(websiteSSL.Pem))
certBlock, _ := pem.Decode([]byte(newSSL.Pem))
if certBlock == nil {
return buserr.New("ErrSSLCertificateFormat")
}
@@ -422,82 +333,18 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
if err != nil {
return err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
newSSL.ExpireDate = cert.NotAfter
newSSL.StartDate = cert.NotBefore
newSSL.Type = cert.Issuer.CommonName
if len(cert.Issuer.Organization) > 0 {
websiteSSL.Organization = cert.Issuer.Organization[0]
newSSL.Organization = cert.Issuer.Organization[0]
} else {
websiteSSL.Organization = cert.Issuer.CommonName
newSSL.Organization = cert.Issuer.CommonName
}
var domains []string
if len(cert.DNSNames) > 0 {
websiteSSL.PrimaryDomain = cert.DNSNames[0]
domains = cert.DNSNames[1:]
newSSL.PrimaryDomain = cert.DNSNames[0]
newSSL.Domains = strings.Join(cert.DNSNames, ",")
}
if len(cert.IPAddresses) > 0 {
if websiteSSL.PrimaryDomain == "" {
websiteSSL.PrimaryDomain = cert.IPAddresses[0].String()
for _, ip := range cert.IPAddresses[1:] {
domains = append(domains, ip.String())
}
} else {
for _, ip := range cert.IPAddresses {
domains = append(domains, ip.String())
}
}
}
websiteSSL.Domains = strings.Join(domains, ",")
if websiteSSL.ID > 0 {
if err := UpdateSSLConfig(*websiteSSL); err != nil {
return err
}
return websiteSSLRepo.Save(websiteSSL)
}
return websiteSSLRepo.Create(context.Background(), websiteSSL)
}
func (w WebsiteSSLService) DownloadFile(id uint) (*os.File, error) {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return nil, err
}
fileOp := files.NewFileOp()
dir := path.Join(global.CONF.System.BaseDir, "1panel/tmp/ssl", websiteSSL.PrimaryDomain)
if fileOp.Stat(dir) {
if err = fileOp.DeleteDir(dir); err != nil {
return nil, err
}
}
if err = fileOp.CreateDir(dir, 0666); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "fullchain.pem"), strings.NewReader(websiteSSL.Pem), 0644); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "privkey.pem"), strings.NewReader(websiteSSL.PrivateKey), 0644); err != nil {
return nil, err
}
fileName := websiteSSL.PrimaryDomain + ".zip"
if err = fileOp.Compress([]string{path.Join(dir, "fullchain.pem"), path.Join(dir, "privkey.pem")}, dir, fileName, files.SdkZip); err != nil {
return nil, err
}
return os.Open(path.Join(dir, fileName))
}
func (w WebsiteSSLService) SyncForRestart() error {
sslList, err := websiteSSLRepo.List()
if err != nil {
return err
}
for _, ssl := range sslList {
if ssl.Status == constant.SSLApply {
ssl.Status = constant.SystemRestart
ssl.Message = "System restart causing interrupt"
_ = websiteSSLRepo.Save(&ssl)
}
}
return nil
return websiteSSLRepo.Create(context.Background(), newSSL)
}
+4 -75
View File
@@ -3,13 +3,11 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"gopkg.in/yaml.v3"
"log"
"path"
"reflect"
"strconv"
@@ -651,9 +649,11 @@ func chownRootDir(path string) error {
return nil
}
func changeServiceName(newComposeContent, newServiceName string) (composeByte []byte, err error) {
func changeServiceName(composePath, newServiceName string) (composeByte []byte, err error) {
composeMap := make(map[string]interface{})
if err = yaml.Unmarshal([]byte(newComposeContent), &composeMap); err != nil {
fileOp := files.NewFileOp()
composeContent, _ := fileOp.GetContent(composePath)
if err = yaml.Unmarshal(composeContent, &composeMap); err != nil {
return
}
value, ok := composeMap["services"]
@@ -748,74 +748,3 @@ func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainM
return
}
func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if websiteSSL.PushDir {
fileOp := files.NewFileOp()
var (
pushErr error
MsgMap = map[string]interface{}{"path": websiteSSL.Dir, "status": i18n.GetMsgByKey("Success")}
)
if pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, "privkey.pem"), websiteSSL.PrivateKey, 0666); pushErr != nil {
MsgMap["status"] = i18n.GetMsgByKey("Failed")
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
logger.Println("Push dir failed:" + pushErr.Error())
}
if pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, "fullchain.pem"), websiteSSL.Pem, 0666); pushErr != nil {
MsgMap["status"] = i18n.GetMsgByKey("Failed")
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
logger.Println("Push dir failed:" + pushErr.Error())
}
if pushErr == nil {
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
}
}
}
func GetSystemSSL() (bool, uint) {
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
return false, 0
}
if sslSetting.Value == "enable" {
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
idValue, _ := strconv.Atoi(sslID.Value)
if idValue > 0 {
return true, uint(idValue)
}
}
return false, 0
}
func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) > 0 {
for _, website := range websites {
if err := createPemFile(website, websiteSSL); err != nil {
return buserr.WithMap("ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}, err)
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return buserr.WithErr(constant.ErrSSLApply, err)
}
}
enable, sslID := GetSystemSSL()
if enable && sslID == websiteSSL.ID {
fileOp := files.NewFileOp()
secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret")
if err := fileOp.WriteFile(path.Join(secretDir, "server.crt"), strings.NewReader(websiteSSL.Pem), 0600); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
return err
}
if err := fileOp.WriteFile(path.Join(secretDir, "server.key"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
return err
}
}
return nil
}
+3 -1
View File
@@ -36,14 +36,16 @@ const (
type AppOperate string
var (
Up AppOperate = "up"
Down AppOperate = "down"
Start AppOperate = "start"
Stop AppOperate = "stop"
Restart AppOperate = "restart"
Delete AppOperate = "delete"
Sync AppOperate = "sync"
Backup AppOperate = "backup"
Restore AppOperate = "restore"
Update AppOperate = "update"
Rebuild AppOperate = "rebuild"
Upgrade AppOperate = "upgrade"
Reload AppOperate = "reload"
)
-1
View File
@@ -11,7 +11,6 @@ const (
MinIo = "MINIO"
Cos = "COS"
Kodo = "KODO"
WebDAV = "WebDAV"
OneDriveRedirectURI = "http://localhost/login/authorized"
)
-5
View File
@@ -6,9 +6,4 @@ const (
DB DBContext = "db"
SystemRestart = "systemRestart"
TypeWebsite = "website"
TypePhp = "php"
TypeSSL = "ssl"
TypeSystem = "system"
)
-1
View File
@@ -16,5 +16,4 @@ var (
RemoteAppResourceDir = path.Join(AppResourceDir, "remote")
RuntimeDir = path.Join(DataDir, "runtime")
RecycleBinDir = "/.1panel_clash"
SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
)
+4 -11
View File
@@ -60,8 +60,6 @@ var (
ErrInstallDirNotFound = "ErrInstallDirNotFound"
ErrContainerName = "ErrContainerName"
ErrAppNameExist = "ErrAppNameExist"
ErrFileNotFound = "ErrFileNotFound"
ErrFileParseApp = "ErrFileParseApp"
)
// website
@@ -78,11 +76,10 @@ var (
// ssl
var (
ErrSSLCannotDelete = "ErrSSLCannotDelete"
ErrAccountCannotDelete = "ErrAccountCannotDelete"
ErrSSLApply = "ErrSSLApply"
ErrEmailIsExist = "ErrEmailIsExist"
ErrEabKidOrEabHmacKeyCannotBlank = "ErrEabKidOrEabHmacKeyCannotBlank"
ErrSSLCannotDelete = "ErrSSLCannotDelete"
ErrAccountCannotDelete = "ErrAccountCannotDelete"
ErrSSLApply = "ErrSSLApply"
ErrEmailIsExist = "ErrEmailIsExist"
)
// file
@@ -144,7 +141,3 @@ var (
var (
ErrBashExecute = "ErrBashExecute"
)
var (
ErrNotExistUser = "ErrNotExistUser"
)
+1
View File
@@ -3,6 +3,7 @@ package constant
const (
StatusRunning = "Running"
StatusDone = "Done"
StatusStoped = "Stoped"
StatusWaiting = "Waiting"
StatusSuccess = "Success"
StatusFailed = "Failed"
-7
View File
@@ -27,7 +27,6 @@ const (
DnsManual = "dnsManual"
Http = "http"
Manual = "manual"
SelfSigned = "selfSigned"
StartWeb = "start"
StopWeb = "stop"
@@ -46,10 +45,4 @@ const (
ConfigPHP = "php"
ConfigFPM = "fpm"
SSLInit = "init"
SSLError = "error"
SSLReady = "ready"
SSLApply = "applying"
SSLApplyError = "applyError"
)
+6 -41
View File
@@ -1,15 +1,10 @@
package job
import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"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/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"path"
"strings"
"time"
)
@@ -21,7 +16,6 @@ func NewSSLJob() *ssl {
}
func (ssl *ssl) Run() {
systemSSLEnable, sslID := service.GetSystemSSL()
sslRepo := repo.NewISSLRepo()
sslService := service.NewIWebsiteSSLService()
sslList, _ := sslRepo.List()
@@ -29,47 +23,18 @@ func (ssl *ssl) Run() {
global.LOG.Info("The scheduled certificate update task is currently in progress ...")
now := time.Now().Add(10 * time.Second)
for _, s := range sslList {
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" || s.Status == "applying" {
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" {
continue
}
expireDate := s.ExpireDate.In(nyc)
sub := expireDate.Sub(now)
if sub.Hours() < 720 {
global.LOG.Infof("Update the SSL certificate for the [%s] domain", s.PrimaryDomain)
if s.Provider == constant.SelfSigned {
caService := service.NewIWebsiteCAService()
if _, err := caService.ObtainSSL(request.WebsiteCAObtain{
ID: s.CaID,
SSLID: s.ID,
Renew: true,
Unit: "year",
Time: 10,
}); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
} else {
if err := sslService.ObtainSSL(request.WebsiteSSLApply{
ID: s.ID,
}); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
global.LOG.Errorf("Update the SSL certificate for the [%s] domain", s.PrimaryDomain)
if err := sslService.Renew(s.ID); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
if systemSSLEnable && sslID == s.ID {
websiteSSL, _ := sslRepo.GetFirst(repo.NewCommonRepo().WithByID(s.ID))
fileOp := files.NewFileOp()
secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret")
if err := fileOp.WriteFile(path.Join(secretDir, "server.crt"), strings.NewReader(websiteSSL.Pem), 0600); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error())
continue
}
if err := fileOp.WriteFile(path.Join(secretDir, "server.key"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error())
continue
}
}
global.LOG.Infof("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
global.LOG.Errorf("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
}
}
global.LOG.Info("The scheduled certificate update task has completed")
+2 -25
View File
@@ -20,9 +20,6 @@ TYPE_RUNTIME: "Runtime environment"
TYPE_DOMAIN: "Domain name"
ErrTypePort: 'Port {{ .name }} format error'
ErrTypePortRange: 'Port range needs to be between 1-65535'
Success: "Success"
Failed: "Failed"
SystemRestart: "System restart causes task interruption"
#app
ErrPortInUsed: "{{ .detail }} port already in use"
@@ -86,7 +83,7 @@ ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
ErrDomainFormat: "{{ .name }} domain format error"
#ssl
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted"
ErrSSLApply: "The certificate continues to be signed successfully, but openresty reload fails, please check the configuration!"
ErrEmailIsExist: 'Email is already exist'
@@ -94,22 +91,6 @@ ErrSSLKeyNotFound: 'The private key file does not exist'
ErrSSLCertificateNotFound: 'The certificate file does not exist'
ErrSSLKeyFormat: 'Private key file verification error'
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be empty'
ErrOpenrestyNotFound: 'Http mode requires Openresty to be installed first'
ApplySSLStart: 'Start applying for certificate, domain name [{{ .domain }}] application method [{{ .type }}] '
dnsAccount: "DNS Automatic"
dnsManual: "DNS Manual"
http: "HTTP"
ApplySSLFailed: 'Application for [{{ .domain }}] certificate failed, {{.detail}} '
ApplySSLSuccess: 'Application for [{{ .domain }}] certificate successful! ! '
DNSAccountName: 'DNS account [{{ .name }}] manufacturer [{{.type}}]'
PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "There is an issued certificate under the current organization and cannot be deleted"
ErrDeleteWithPanelSSL: "Panel SSL configuration uses this certificate and cannot be deleted"
ErrDefaultCA: "The default organization cannot be deleted"
ApplyWebSiteSSLLog: "Start updating {{ .name }} website certificate"
ErrUpdateWebsiteSSL: "{{ .name }} website failed to update certificate: {{ .err }}"
ApplyWebSiteSSLSuccess: "Update website certificate successfully"
#mysql
ErrUserIsExist: "The current user already exists. Please enter a new user"
@@ -156,8 +137,4 @@ ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}"
ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!"
#cronjob
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
#toolbox
ErrNotExistUser: "The current user does not exist. Please modify and retry!"
ErrBanAction: "Setting failed, the current {{ .name }} service is unavailable, please check and try again!"
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
+2 -26
View File
@@ -20,9 +20,6 @@ TYPE_RUNTIME: "運作環境"
TYPE_DOMAIN: "網域名稱"
ErrTypePort: '埠 {{ .name }} 格式錯誤'
ErrTypePortRange: '連接埠範圍需要在 1-65535 之間'
Success: "成功"
Failed: "失敗"
SystemRestart: "系統重啟導致任務中斷"
#app
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
@@ -86,7 +83,7 @@ ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正確"
#ssl
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
ErrAccountCannotDelete: "帳號關聯證書,無法刪除"
ErrSSLApply: "證書續簽成功,openresty reload失敗,請檢查配置!"
ErrEmailIsExist: '郵箱已存在'
@@ -94,23 +91,6 @@ ErrSSLKeyNotFound: '私鑰文件不存在'
ErrSSLCertificateNotFound: '證書文件不存在'
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
ErrOpenrestyNotFound: 'Http 模式需要先安裝 Openresty'
ApplySSLStart: '開始申請憑證,網域 [{{ .domain }}] 申請方式 [{{ .type }}] '
dnsAccount: "DNS 自動"
dnsManual: "DNS 手排"
http: "HTTP"
ApplySSLFailed: '申請 [{{ .domain }}] 憑證失敗, {{.detail}} '
ApplySSLSuccess: '申請 [{{ .domain }}] 憑證成功! ! '
DNSAccountName: 'DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]'
PushDirLog: '憑證推送到目錄 [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "目前機構下存在已簽發證書,無法刪除"
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此證書,無法刪除"
ErrDefaultCA: "默認機構不能刪除"
ApplyWebSiteSSLLog: "開始更新 {{ .name }} 網站憑證"
ErrUpdateWebsiteSSL: "{{ .name }} 網站更新憑證失敗: {{ .err }}"
ApplyWebSiteSSLSuccess: "更新網站憑證成功"
#mysql
ErrUserIsExist: "當前用戶已存在,請重新輸入"
@@ -157,8 +137,4 @@ ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}"
ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
#cronjob
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
#toolbox
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!"
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
+1 -24
View File
@@ -20,9 +20,6 @@ TYPE_RUNTIME: "运行环境"
TYPE_DOMAIN: "域名"
ErrTypePort: '端口 {{ .name }} 格式错误'
ErrTypePortRange: '端口范围需要在 1-65535 之间'
Success: "成功"
Failed: "失败"
SystemRestart: "系统重启导致任务中断"
#app
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
@@ -86,7 +83,7 @@ ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正确"
#ssl
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
ErrAccountCannotDelete: "账号关联证书,无法删除"
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
ErrEmailIsExist: '邮箱已存在'
@@ -94,22 +91,6 @@ ErrSSLKeyNotFound: '私钥文件不存在'
ErrSSLCertificateNotFound: '证书文件不存在'
ErrSSLKeyFormat: '私钥文件校验失败'
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'
ErrOpenrestyNotFound: 'Http 模式需要首先安装 Openresty'
ApplySSLStart: '开始申请证书,域名 [{{ .domain }}] 申请方式 [{{ .type }}] '
dnsAccount: "DNS 自动"
dnsManual: "DNS 手动"
http: "HTTP"
ApplySSLFailed: '申请 [{{ .domain }}] 证书失败, {{.detail}} '
ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功!!'
DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{.type}}]'
PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "当前机构下存在已签发证书,无法删除"
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此证书,无法删除"
ErrDefaultCA: "默认机构不能删除"
ApplyWebSiteSSLLog: "开始更新 {{ .name }} 网站证书"
ErrUpdateWebsiteSSL: "{{ .name }} 网站更新证书失败: {{ .err }}"
ApplyWebSiteSSLSuccess: "更新网站证书成功"
#mysql
ErrUserIsExist: "当前用户已存在,请重新输入"
@@ -157,7 +138,3 @@ ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后
#cronjob
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
#toolbox
ErrNotExistUser: "当前用户不存在,请修改后重试!"
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
+1 -3
View File
@@ -20,10 +20,8 @@ func Init() {
constant.LocalAppInstallDir = path.Join(constant.AppInstallDir, "local")
constant.RemoteAppResourceDir = path.Join(constant.AppResourceDir, "remote")
constant.SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir,
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir, constant.SSLLogDir}
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir}
fileOp := files.NewFileOp()
for _, dir := range dirs {
-7
View File
@@ -10,7 +10,6 @@ func Init() {
go syncApp()
go syncInstalledApp()
go syncRuntime()
go syncSSL()
}
func syncApp() {
@@ -32,9 +31,3 @@ func syncRuntime() {
global.LOG.Errorf("sync runtime status error : %s", err.Error())
}
}
func syncSSL() {
if err := service.NewIWebsiteSSLService().SyncForRestart(); err != nil {
global.LOG.Errorf("sync ssl status error : %s", err.Error())
}
}
-8
View File
@@ -52,14 +52,6 @@ func Init() {
migrations.AddBindAddress,
migrations.AddCommandGroup,
migrations.AddAppSyncStatus,
migrations.UpdateAcmeAccount,
migrations.UpdateWebsiteSSL,
migrations.AddWebsiteCA,
migrations.AddDockerSockPath,
migrations.AddDatabaseSSL,
migrations.AddDefaultCA,
migrations.AddSettingRecycleBin,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
@@ -1,89 +0,0 @@
package migrations
import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
var UpdateAcmeAccount = &gormigrate.Migration{
ID: "20231117-update-acme-account",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteAcmeAccount{}); err != nil {
return err
}
return nil
},
}
var AddWebsiteCA = &gormigrate.Migration{
ID: "20231125-add-website-ca",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteCA{}); err != nil {
return err
}
return nil
},
}
var UpdateWebsiteSSL = &gormigrate.Migration{
ID: "20231128-update-website-ssl",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err
}
return nil
},
}
var AddDockerSockPath = &gormigrate.Migration{
ID: "20231128-add-docker-sock-path",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "DockerSockPath", Value: "unix:///var/run/docker.sock"}).Error; err != nil {
return err
}
return nil
},
}
var AddDatabaseSSL = &gormigrate.Migration{
ID: "20231126-add-database-ssl",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Database{}); err != nil {
return err
}
return nil
},
}
var AddDefaultCA = &gormigrate.Migration{
ID: "20231129-add-default-ca",
Migrate: func(tx *gorm.DB) error {
caService := service.NewIWebsiteCAService()
if _, err := caService.Create(request.WebsiteCACreate{
CommonName: "1Panel-CA",
Country: "CN",
KeyType: "P256",
Name: "1Panel",
Organization: "FIT2CLOUD",
OrganizationUint: "1Panel",
Province: "Beijing",
City: "Beijing",
}); err != nil {
return err
}
return nil
},
}
var AddSettingRecycleBin = &gormigrate.Migration{
ID: "20231129-add-setting-recycle-bin",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "FileRecycleBin", Value: "enable"}).Error; err != nil {
return err
}
return nil
},
}
+4 -4
View File
@@ -1,10 +1,10 @@
package router
import (
"fmt"
"github.com/gin-contrib/gzip"
"html/template"
"net/http"
"strings"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
@@ -21,10 +21,12 @@ import (
func setWebStatic(rootRouter *gin.RouterGroup) {
rootRouter.StaticFS("/public", http.FS(web.Favicon))
rootRouter.Use(func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/assets/") {
c.Header("Cache-Control", "max-age=31536000")
}
c.Next()
})
rootRouter.GET("/assets/*filepath", func(c *gin.Context) {
c.Writer.Header().Set("Cache-Control", fmt.Sprintf("private, max-age=%d", 3600))
staticServer := http.FileServer(http.FS(web.Assets))
staticServer.ServeHTTP(c.Writer, c.Request)
})
@@ -92,8 +94,6 @@ func Routers() *gin.Engine {
systemRouter.InitNginxRouter(PrivateGroup)
systemRouter.InitRuntimeRouter(PrivateGroup)
systemRouter.InitProcessRouter(PrivateGroup)
systemRouter.InitToolboxRouter(PrivateGroup)
systemRouter.InitWebsiteCARouter(PrivateGroup)
}
return Router
-1
View File
@@ -8,7 +8,6 @@ type RouterGroup struct {
MonitorRouter
LogRouter
FileRouter
ToolboxRouter
TerminalRouter
CronjobRouter
SettingRouter
-1
View File
@@ -79,7 +79,6 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.POST("/docker/operate", baseApi.OperateDocker)
baRouter.POST("/daemonjson/update", baseApi.UpdateDaemonJson)
baRouter.POST("/logoption/update", baseApi.UpdateLogOption)
baRouter.POST("/ipv6option/update", baseApi.UpdateIpv6Option)
baRouter.POST("/daemonjson/update/byfile", baseApi.UpdateDaemonJsonByFile)
}
}
-1
View File
@@ -43,7 +43,6 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
fileRouter.POST("/recycle/search", baseApi.SearchRecycleBinFile)
fileRouter.POST("/recycle/reduce", baseApi.ReduceRecycleBinFile)
fileRouter.POST("/recycle/clear", baseApi.ClearRecycleBinFile)
fileRouter.GET("/recycle/status", baseApi.GetRecycleStatus)
fileRouter.POST("/favorite/search", baseApi.SearchFavorite)
fileRouter.POST("/favorite", baseApi.CreateFavorite)
+4
View File
@@ -29,9 +29,13 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
settingRouter.POST("/ssl/download", baseApi.DownloadSSL)
settingRouter.POST("/password/update", baseApi.UpdatePassword)
settingRouter.GET("/time/option", baseApi.LoadTimeZone)
settingRouter.POST("/time/sync", baseApi.SyncTime)
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
settingRouter.POST("/mfa", baseApi.LoadMFA)
settingRouter.POST("/mfa/bind", baseApi.MFABind)
settingRouter.POST("/scan", baseApi.ScanSystem)
settingRouter.POST("/clean", baseApi.SystemClean)
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
settingRouter.POST("/snapshot/status", baseApi.LoadSnapShotStatus)
-40
View File
@@ -1,40 +0,0 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
type ToolboxRouter struct{}
func (s *ToolboxRouter) InitToolboxRouter(Router *gin.RouterGroup) {
toolboxRouter := Router.Group("toolbox").
Use(middleware.JwtAuth()).
Use(middleware.SessionAuth()).
Use(middleware.PasswordExpired())
baseApi := v1.ApiGroupApp.BaseApi
{
toolboxRouter.POST("/device/base", baseApi.LoadDeviceBaseInfo)
toolboxRouter.GET("/device/zone/options", baseApi.LoadTimeOption)
toolboxRouter.POST("/device/update/conf", baseApi.UpdateDeviceConf)
toolboxRouter.POST("/device/update/host", baseApi.UpdateDeviceHost)
toolboxRouter.POST("/device/update/passwd", baseApi.UpdateDevicePasswd)
toolboxRouter.POST("/device/update/swap", baseApi.UpdateDeviceSwap)
toolboxRouter.POST("/device/update/byconf", baseApi.UpdateDeviceByFile)
toolboxRouter.POST("/device/check/dns", baseApi.CheckDNS)
toolboxRouter.POST("/device/conf", baseApi.LoadDeviceConf)
toolboxRouter.POST("/scan", baseApi.ScanSystem)
toolboxRouter.POST("/clean", baseApi.SystemClean)
toolboxRouter.GET("/fail2ban/base", baseApi.LoadFail2BanBaseInfo)
toolboxRouter.GET("/fail2ban/load/conf", baseApi.LoadFail2BanConf)
toolboxRouter.POST("/fail2ban/search", baseApi.SearchFail2Ban)
toolboxRouter.POST("/fail2ban/operate", baseApi.OperateFail2Ban)
toolboxRouter.POST("/fail2ban/operate/sshd", baseApi.OperateSSHD)
toolboxRouter.POST("/fail2ban/update", baseApi.UpdateFail2BanConf)
toolboxRouter.POST("/fail2ban/update/byconf", baseApi.UpdateFail2BanConfByFile)
}
}
-22
View File
@@ -1,22 +0,0 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
func (a *WebsiteDnsAccountRouter) InitWebsiteCARouter(Router *gin.RouterGroup) {
groupRouter := Router.Group("websites/ca")
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("/search", baseApi.PageWebsiteCA)
groupRouter.POST("", baseApi.CreateWebsiteCA)
groupRouter.POST("/del", baseApi.DeleteWebsiteCA)
groupRouter.POST("/obtain", baseApi.ObtainWebsiteCA)
groupRouter.POST("/renew", baseApi.RenewWebsiteCA)
groupRouter.GET("/:id", baseApi.GetWebsiteCA)
}
}
+1 -2
View File
@@ -16,6 +16,7 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("/search", baseApi.PageWebsiteSSL)
groupRouter.POST("/renew", baseApi.RenewWebsiteSSL)
groupRouter.POST("", baseApi.CreateWebsiteSSL)
groupRouter.POST("/resolve", baseApi.GetDNSResolve)
groupRouter.POST("/del", baseApi.DeleteWebsiteSSL)
@@ -23,7 +24,5 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
groupRouter.GET("/:id", baseApi.GetWebsiteSSLById)
groupRouter.POST("/update", baseApi.UpdateWebsiteSSL)
groupRouter.POST("/upload", baseApi.UploadWebsiteSSL)
groupRouter.POST("/obtain", baseApi.ApplyWebsiteSSL)
groupRouter.POST("/download", baseApi.DownloadWebsiteSSL)
}
}
+1 -2
View File
@@ -32,9 +32,9 @@ import (
func Start() {
viper.Init()
log.Init()
app.Init()
db.Init()
migration.Init()
app.Init()
validator.Init()
gob.Register(psession.SessionUser{})
cache.Init()
@@ -86,7 +86,6 @@ func Start() {
panic(err)
}
} else {
global.LOG.Infof("listen at http://%s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem)
if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
panic(err)
}
+1 -1
View File
@@ -34,7 +34,7 @@ func CreateCaptcha() (*dto.CaptchaResponse, error) {
driverString.Fonts = []string{"RitaSmith.ttf", "actionj.ttf", "chromohv.ttf"}
driver := driverString.ConvertFonts()
c := base64Captcha.NewCaptcha(driver, store)
id, b64s, _, err := c.Generate()
id, b64s, err := c.Generate()
if err != nil {
return nil, err
}
+87 -46
View File
@@ -7,24 +7,44 @@ import (
"net/url"
"os"
"github.com/1Panel-dev/1Panel/backend/constant"
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
)
type cosClient struct {
scType string
client *cosSDK.Client
clientWithBucket *cosSDK.Client
region string
accessKey string
secretKey string
scType string
Vars map[string]interface{}
client *cosSDK.Client
}
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
region := loadParamFromVars("region", true, vars)
accessKey := loadParamFromVars("accessKey", true, vars)
secretKey := loadParamFromVars("secretKey", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
scType := loadParamFromVars("scType", true, vars)
if len(scType) == 0 {
var accessKey string
var secretKey string
var scType string
var region string
if _, ok := vars["region"]; ok {
region = vars["region"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["accessKey"]; ok {
accessKey = vars["accessKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["scType"]; ok {
scType = vars["scType"].(string)
} else {
scType = "Standard"
}
if _, ok := vars["secretKey"]; ok {
secretKey = vars["secretKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
b := &cosSDK.BaseURL{BucketURL: u}
@@ -35,23 +55,11 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
},
})
if len(bucket) != 0 {
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
b2 := &cosSDK.BaseURL{BucketURL: u2}
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
Transport: &cosSDK.AuthorizationTransport{
SecretID: accessKey,
SecretKey: secretKey,
},
})
return &cosClient{client: client, clientWithBucket: clientWithBucket, scType: scType}, nil
}
return &cosClient{client: client, clientWithBucket: nil, scType: scType}, nil
return &cosClient{Vars: vars, client: client, accessKey: accessKey, secretKey: secretKey, scType: scType, region: region}, nil
}
func (c cosClient) ListBuckets() ([]interface{}, error) {
buckets, _, err := c.client.Service.Get(context.Background())
func (cos cosClient) ListBuckets() ([]interface{}, error) {
buckets, _, err := cos.client.Service.Get(context.Background())
if err != nil {
return nil, err
}
@@ -62,33 +70,34 @@ func (c cosClient) ListBuckets() ([]interface{}, error) {
return datas, nil
}
func (c cosClient) Exist(path string) (bool, error) {
exist, err := c.clientWithBucket.Object.IsExist(context.Background(), path)
func (cos cosClient) Exist(path string) (bool, error) {
client, err := cos.newClientWithBucket()
if err != nil {
return false, err
}
exist, err := client.Object.IsExist(context.Background(), path)
if err != nil {
return false, err
}
return exist, nil
}
func (c cosClient) Size(path string) (int64, error) {
data, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: path})
func (cos cosClient) Delete(path string) (bool, error) {
client, err := cos.newClientWithBucket()
if err != nil {
return 0, err
return false, err
}
if len(data.Contents) == 0 {
return 0, fmt.Errorf("no such file %s", path)
}
return data.Contents[0].Size, nil
}
func (c cosClient) Delete(path string) (bool, error) {
if _, err := c.clientWithBucket.Object.Delete(context.Background(), path); err != nil {
if _, err := client.Object.Delete(context.Background(), path); err != nil {
return false, err
}
return true, nil
}
func (c cosClient) Upload(src, target string) (bool, error) {
func (cos cosClient) Upload(src, target string) (bool, error) {
client, err := cos.newClientWithBucket()
if err != nil {
return false, err
}
fileInfo, err := os.Stat(src)
if err != nil {
return false, err
@@ -98,22 +107,22 @@ func (c cosClient) Upload(src, target string) (bool, error) {
OptIni: &cosSDK.InitiateMultipartUploadOptions{
ACLHeaderOptions: nil,
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
XCosStorageClass: c.scType,
XCosStorageClass: cos.scType,
},
},
PartSize: 200,
}
if _, _, err := c.clientWithBucket.Object.MultiUpload(
if _, _, err := client.Object.MultiUpload(
context.Background(), target, src, opt,
); err != nil {
return false, err
}
return true, nil
}
if _, err := c.clientWithBucket.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
if _, err := client.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
ACLHeaderOptions: nil,
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
XCosStorageClass: c.scType,
XCosStorageClass: cos.scType,
},
}); err != nil {
return false, err
@@ -121,15 +130,31 @@ func (c cosClient) Upload(src, target string) (bool, error) {
return true, nil
}
func (c cosClient) Download(src, target string) (bool, error) {
if _, err := c.clientWithBucket.Object.Download(context.Background(), src, target, &cosSDK.MultiDownloadOptions{}); err != nil {
func (cos cosClient) Download(src, target string) (bool, error) {
client, err := cos.newClientWithBucket()
if err != nil {
return false, err
}
if _, err := client.Object.Download(context.Background(), src, target, &cosSDK.MultiDownloadOptions{}); err != nil {
return false, err
}
return true, nil
}
func (c cosClient) ListObjects(prefix string) ([]string, error) {
datas, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: prefix})
func (cos *cosClient) GetBucket() (string, error) {
if _, ok := cos.Vars["bucket"]; ok {
return cos.Vars["bucket"].(string), nil
} else {
return "", constant.ErrInvalidParams
}
}
func (cos cosClient) ListObjects(prefix string) ([]string, error) {
client, err := cos.newClientWithBucket()
if err != nil {
return nil, err
}
datas, _, err := client.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: prefix})
if err != nil {
return nil, err
}
@@ -140,3 +165,19 @@ func (c cosClient) ListObjects(prefix string) ([]string, error) {
}
return result, nil
}
func (cos cosClient) newClientWithBucket() (*cosSDK.Client, error) {
bucket, err := cos.GetBucket()
if err != nil {
return nil, err
}
u, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, cos.region))
b := &cosSDK.BaseURL{BucketURL: u}
client := cosSDK.NewClient(b, &http.Client{
Transport: &cosSDK.AuthorizationTransport{
SecretID: cos.accessKey,
SecretKey: cos.secretKey,
},
})
return client, nil
}
@@ -1,20 +0,0 @@
package client
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/global"
)
func loadParamFromVars(key string, isString bool, vars map[string]interface{}) string {
if _, ok := vars[key]; !ok {
if key != "bucket" {
global.LOG.Errorf("load param %s from vars failed, err: not exist!", key)
}
return ""
}
if isString {
return vars[key].(string)
}
return fmt.Sprintf("%v", vars[key].(float64))
}
+68 -35
View File
@@ -4,35 +4,45 @@ import (
"context"
"time"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/qiniu/go-sdk/v7/auth"
"github.com/qiniu/go-sdk/v7/auth/qbox"
"github.com/qiniu/go-sdk/v7/storage"
)
type kodoClient struct {
bucket string
domain string
auth *auth.Credentials
client *storage.BucketManager
accessKey string
secretKey string
Vars map[string]interface{}
client *storage.BucketManager
}
func NewKodoClient(vars map[string]interface{}) (*kodoClient, error) {
accessKey := loadParamFromVars("accessKey", true, vars)
secretKey := loadParamFromVars("secretKey", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
domain := loadParamFromVars("domain", true, vars)
var accessKey string
var secretKey string
if _, ok := vars["accessKey"]; ok {
accessKey = vars["accessKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["secretKey"]; ok {
secretKey = vars["secretKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
conn := auth.New(accessKey, secretKey)
conn := qbox.NewMac(accessKey, secretKey)
cfg := storage.Config{
UseHTTPS: false,
}
bucketManager := storage.NewBucketManager(conn, &cfg)
return &kodoClient{client: bucketManager, auth: conn, bucket: bucket, domain: domain}, nil
return &kodoClient{Vars: vars, client: bucketManager, accessKey: accessKey, secretKey: secretKey}, nil
}
func (k kodoClient) ListBuckets() ([]interface{}, error) {
buckets, err := k.client.Buckets(true)
func (kodo kodoClient) ListBuckets() ([]interface{}, error) {
buckets, err := kodo.client.Buckets(true)
if err != nil {
return nil, err
}
@@ -43,33 +53,38 @@ func (k kodoClient) ListBuckets() ([]interface{}, error) {
return datas, nil
}
func (k kodoClient) Exist(path string) (bool, error) {
if _, err := k.client.Stat(k.bucket, path); err != nil {
return false, err
}
return true, nil
}
func (k kodoClient) Size(path string) (int64, error) {
file, err := k.client.Stat(k.bucket, path)
func (kodo kodoClient) Exist(path string) (bool, error) {
bucket, err := kodo.GetBucket()
if err != nil {
return 0, err
return false, err
}
return file.Fsize, nil
}
func (k kodoClient) Delete(path string) (bool, error) {
if err := k.client.Delete(k.bucket, path); err != nil {
if _, err := kodo.client.Stat(bucket, path); err != nil {
return false, err
}
return true, nil
}
func (k kodoClient) Upload(src, target string) (bool, error) {
putPolicy := storage.PutPolicy{
Scope: k.bucket,
func (kodo kodoClient) Delete(path string) (bool, error) {
bucket, err := kodo.GetBucket()
if err != nil {
return false, err
}
upToken := putPolicy.UploadToken(k.auth)
if err := kodo.client.Delete(bucket, path); err != nil {
return false, err
}
return true, nil
}
func (kodo kodoClient) Upload(src, target string) (bool, error) {
bucket, err := kodo.GetBucket()
if err != nil {
return false, err
}
putPolicy := storage.PutPolicy{
Scope: bucket,
}
mac := qbox.NewMac(kodo.accessKey, kodo.secretKey)
upToken := putPolicy.UploadToken(mac)
cfg := storage.Config{UseHTTPS: true, UseCdnDomains: false}
resumeUploader := storage.NewResumeUploaderV2(&cfg)
ret := storage.PutRet{}
@@ -80,9 +95,14 @@ func (k kodoClient) Upload(src, target string) (bool, error) {
return true, nil
}
func (k kodoClient) Download(src, target string) (bool, error) {
func (kodo kodoClient) Download(src, target string) (bool, error) {
mac := auth.New(kodo.accessKey, kodo.secretKey)
if _, ok := kodo.Vars["domain"]; !ok {
return false, constant.ErrInvalidParams
}
domain := kodo.Vars["domain"].(string)
deadline := time.Now().Add(time.Second * 3600).Unix()
privateAccessURL := storage.MakePrivateURL(k.auth, k.domain, src, deadline)
privateAccessURL := storage.MakePrivateURL(mac, domain, src, deadline)
fo := files.NewFileOp()
if err := fo.DownloadFile(privateAccessURL, target); err != nil {
@@ -91,11 +111,24 @@ func (k kodoClient) Download(src, target string) (bool, error) {
return true, nil
}
func (k kodoClient) ListObjects(prefix string) ([]string, error) {
func (kodo *kodoClient) GetBucket() (string, error) {
if _, ok := kodo.Vars["bucket"]; ok {
return kodo.Vars["bucket"].(string), nil
} else {
return "", constant.ErrInvalidParams
}
}
func (kodo kodoClient) ListObjects(prefix string) ([]string, error) {
bucket, err := kodo.GetBucket()
if err != nil {
return nil, constant.ErrInvalidParams
}
var result []string
marker := ""
for {
entries, _, nextMarker, hashNext, err := k.client.ListFiles(k.bucket, prefix, "", marker, 1000)
entries, _, nextMarker, hashNext, err := kodo.client.ListFiles(bucket, prefix, "", marker, 1000)
if err != nil {
return nil, err
}
+91 -53
View File
@@ -14,15 +14,29 @@ import (
)
type minIoClient struct {
bucket string
Vars map[string]interface{}
client *minio.Client
}
func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {
endpoint := loadParamFromVars("endpoint", true, vars)
accessKeyID := loadParamFromVars("accessKey", true, vars)
secretAccessKey := loadParamFromVars("secretKey", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
var endpoint string
var accessKeyID string
var secretAccessKey string
if _, ok := vars["endpoint"]; ok {
endpoint = vars["endpoint"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["accessKey"]; ok {
accessKeyID = vars["accessKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["secretKey"]; ok {
secretAccessKey = vars["secretKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
ssl := strings.Split(endpoint, ":")[0]
if len(ssl) == 0 || (ssl != "https" && ssl != "http") {
return nil, constant.ErrInvalidParams
@@ -45,11 +59,14 @@ func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {
if err != nil {
return nil, err
}
return &minIoClient{bucket: bucket, client: client}, nil
return &minIoClient{
client: client,
Vars: vars,
}, nil
}
func (m minIoClient) ListBuckets() ([]interface{}, error) {
buckets, err := m.client.ListBuckets(context.Background())
func (minIo minIoClient) ListBuckets() ([]interface{}, error) {
buckets, err := minIo.client.ListBuckets(context.Background())
if err != nil {
return nil, err
}
@@ -60,44 +77,49 @@ func (m minIoClient) ListBuckets() ([]interface{}, error) {
return result, err
}
func (m minIoClient) Exist(path string) (bool, error) {
if _, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{}); err != nil {
return false, err
func (minIo minIoClient) Exist(path string) (bool, error) {
if _, ok := minIo.Vars["bucket"]; ok {
_, err := minIo.client.GetObject(context.Background(), minIo.Vars["bucket"].(string), path, minio.GetObjectOptions{})
if err != nil {
return false, err
}
return true, nil
} else {
return false, constant.ErrInvalidParams
}
return true, nil
}
func (m minIoClient) Size(path string) (int64, error) {
obj, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{})
if err != nil {
return 0, err
func (minIo minIoClient) Delete(path string) (bool, error) {
if _, ok := minIo.Vars["bucket"]; ok {
object, err := minIo.client.GetObject(context.Background(), minIo.Vars["bucket"].(string), path, minio.GetObjectOptions{})
if err != nil {
return false, err
}
info, err := object.Stat()
if err != nil {
return false, err
}
err = minIo.client.RemoveObject(context.Background(), minIo.Vars["bucket"].(string), path, minio.RemoveObjectOptions{
GovernanceBypass: true,
VersionID: info.VersionID,
})
if err != nil {
return false, err
}
return true, nil
} else {
return false, constant.ErrInvalidParams
}
file, err := obj.Stat()
if err != nil {
return 0, err
}
return file.Size, nil
}
func (m minIoClient) Delete(path string) (bool, error) {
object, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{})
if err != nil {
return false, err
func (minIo minIoClient) Upload(src, target string) (bool, error) {
var bucket string
if _, ok := minIo.Vars["bucket"]; ok {
bucket = minIo.Vars["bucket"].(string)
} else {
return false, constant.ErrInvalidParams
}
info, err := object.Stat()
if err != nil {
return false, err
}
if err = m.client.RemoveObject(context.Background(), m.bucket, path, minio.RemoveObjectOptions{
GovernanceBypass: true,
VersionID: info.VersionID,
}); err != nil {
return false, err
}
return true, nil
}
func (m minIoClient) Upload(src, target string) (bool, error) {
file, err := os.Open(src)
if err != nil {
return false, err
@@ -108,36 +130,52 @@ func (m minIoClient) Upload(src, target string) (bool, error) {
if err != nil {
return false, err
}
_, err = m.client.PutObject(context.Background(), m.bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
_, err = minIo.client.PutObject(context.Background(), bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
return false, err
}
return true, nil
}
func (m minIoClient) Download(src, target string) (bool, error) {
object, err := m.client.GetObject(context.Background(), m.bucket, src, minio.GetObjectOptions{})
if err != nil {
return false, err
func (minIo minIoClient) Download(src, target string) (bool, error) {
if _, ok := minIo.Vars["bucket"]; ok {
object, err := minIo.client.GetObject(context.Background(), minIo.Vars["bucket"].(string), src, minio.GetObjectOptions{})
if err != nil {
return false, err
}
localFile, err := os.Create(target)
if err != nil {
return false, err
}
if _, err = io.Copy(localFile, object); err != nil {
return false, err
}
return true, nil
} else {
return false, constant.ErrInvalidParams
}
localFile, err := os.Create(target)
if err != nil {
return false, err
}
if _, err = io.Copy(localFile, object); err != nil {
return false, err
}
return true, nil
}
func (m minIoClient) ListObjects(prefix string) ([]string, error) {
func (minIo *minIoClient) GetBucket() (string, error) {
if _, ok := minIo.Vars["bucket"]; ok {
return minIo.Vars["bucket"].(string), nil
} else {
return "", constant.ErrInvalidParams
}
}
func (minIo minIoClient) ListObjects(prefix string) ([]string, error) {
bucket, err := minIo.GetBucket()
if err != nil {
return nil, constant.ErrInvalidParams
}
opts := minio.ListObjectsOptions{
Recursive: true,
Prefix: prefix,
}
var result []string
for object := range m.client.ListObjects(context.Background(), m.bucket, opts) {
for object := range minIo.client.ListObjects(context.Background(), bucket, opts) {
if object.Err != nil {
continue
}
+38 -56
View File
@@ -23,11 +23,17 @@ import (
)
type oneDriveClient struct {
Vars map[string]interface{}
client odsdk.Client
}
func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
token := loadParamFromVars("accessToken", true, vars)
token := ""
if _, ok := vars["accessToken"]; ok {
token = vars["accessToken"].(string)
} else {
return nil, constant.ErrInvalidParams
}
ctx := context.Background()
newToken, err := refreshToken(token)
@@ -45,13 +51,13 @@ func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
return &oneDriveClient{client: *client}, nil
}
func (o oneDriveClient) ListBuckets() ([]interface{}, error) {
func (onedrive oneDriveClient) ListBuckets() ([]interface{}, error) {
return nil, nil
}
func (o oneDriveClient) Exist(path string) (bool, error) {
func (onedrive oneDriveClient) Exist(path string) (bool, error) {
path = "/" + strings.TrimPrefix(path, "/")
fileID, err := o.loadIDByPath(path)
fileID, err := onedrive.loadIDByPath(path)
if err != nil {
return false, err
}
@@ -59,50 +65,26 @@ func (o oneDriveClient) Exist(path string) (bool, error) {
return len(fileID) != 0, nil
}
func (o oneDriveClient) Size(path string) (int64, error) {
func (onedrive oneDriveClient) Delete(path string) (bool, error) {
path = "/" + strings.TrimPrefix(path, "/")
pathItem := "root:" + path
if path == "/" {
pathItem = "root"
}
req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
if err != nil {
return 0, fmt.Errorf("new request for file id failed, err: %v", err)
}
var driveItem myDriverItem
if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {
return 0, fmt.Errorf("do request for file id failed, err: %v", err)
}
return driveItem.Size, nil
}
type myDriverItem struct {
Name string `json:"name"`
Id string `json:"id"`
Size int64 `json:"size"`
}
func (o oneDriveClient) Delete(path string) (bool, error) {
path = "/" + strings.TrimPrefix(path, "/")
req, err := o.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil)
req, err := onedrive.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil)
if err != nil {
return false, fmt.Errorf("new request for delete file failed, err: %v \n", err)
}
if err := o.client.Do(context.Background(), req, false, nil); err != nil {
if err := onedrive.client.Do(context.Background(), req, false, nil); err != nil {
return false, fmt.Errorf("do request for delete file failed, err: %v \n", err)
}
return true, nil
}
func (o oneDriveClient) Upload(src, target string) (bool, error) {
func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
target = "/" + strings.TrimPrefix(target, "/")
if _, err := o.loadIDByPath(path.Dir(target)); err != nil {
if _, err := onedrive.loadIDByPath(path.Dir(target)); err != nil {
if !strings.Contains(err.Error(), "itemNotFound") {
return false, err
}
if err := o.createFolder(path.Dir(target)); err != nil {
if err := onedrive.createFolder(path.Dir(target)); err != nil {
return false, fmt.Errorf("create dir before upload failed, err: %v", err)
}
}
@@ -123,7 +105,7 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
fileName := fileInfo.Name()
fileSize := fileInfo.Size()
folderID, err := o.loadIDByPath(path.Dir(target))
folderID, err := onedrive.loadIDByPath(path.Dir(target))
if err != nil {
return false, err
}
@@ -137,13 +119,13 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
DeferCommit bool `json:"deferCommit"`
}{sessionCreationRequestInside, false}
sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest)
sessionCreationReq, err := onedrive.client.NewRequest("POST", apiURL, sessionCreationRequest)
if err != nil {
return false, err
}
var sessionCreationResp *NewUploadSessionCreationResponse
err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
err = onedrive.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
if err != nil {
return false, fmt.Errorf("session creation failed %w", err)
}
@@ -167,11 +149,11 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
bufferLast := buffer[:length]
buffer = bufferLast
}
sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
sessionFileUploadReq, err := onedrive.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
if err != nil {
return false, err
}
if err := o.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
if err := onedrive.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
return false, err
}
}
@@ -182,14 +164,14 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
return true, nil
}
func (o oneDriveClient) Download(src, target string) (bool, error) {
func (onedrive oneDriveClient) Download(src, target string) (bool, error) {
src = "/" + strings.TrimPrefix(src, "/")
req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/root:%s", src), nil)
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/root:%s", src), nil)
if err != nil {
return false, fmt.Errorf("new request for file id failed, err: %v", err)
}
var driveItem *odsdk.DriveItem
if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {
if err := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
return false, fmt.Errorf("do request for file id failed, err: %v", err)
}
@@ -214,19 +196,19 @@ func (o oneDriveClient) Download(src, target string) (bool, error) {
return true, nil
}
func (o *oneDriveClient) ListObjects(prefix string) ([]string, error) {
func (onedrive *oneDriveClient) ListObjects(prefix string) ([]string, error) {
prefix = "/" + strings.TrimPrefix(prefix, "/")
folderID, err := o.loadIDByPath(prefix)
folderID, err := onedrive.loadIDByPath(prefix)
if err != nil {
return nil, err
}
req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/items/%s/children", folderID), nil)
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/items/%s/children", folderID), nil)
if err != nil {
return nil, fmt.Errorf("new request for list failed, err: %v", err)
}
var driveItems *odsdk.OneDriveDriveItemsResponse
if err := o.client.Do(context.Background(), req, false, &driveItems); err != nil {
if err := onedrive.client.Do(context.Background(), req, false, &driveItems); err != nil {
return nil, fmt.Errorf("do request for list failed, err: %v", err)
}
@@ -237,17 +219,17 @@ func (o *oneDriveClient) ListObjects(prefix string) ([]string, error) {
return itemList, nil
}
func (o *oneDriveClient) loadIDByPath(path string) (string, error) {
func (onedrive *oneDriveClient) loadIDByPath(path string) (string, error) {
pathItem := "root:" + path
if path == "/" {
pathItem = "root"
}
req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
if err != nil {
return "", fmt.Errorf("new request for file id failed, err: %v", err)
}
var driveItem *odsdk.DriveItem
if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {
if err := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
return "", fmt.Errorf("do request for file id failed, err: %v", err)
}
return driveItem.Id, nil
@@ -287,18 +269,18 @@ func refreshToken(oldToken string) (string, error) {
return accessToken, nil
}
func (o *oneDriveClient) createFolder(parent string) error {
if _, err := o.loadIDByPath(path.Dir(parent)); err != nil {
func (onedrive *oneDriveClient) createFolder(parent string) error {
if _, err := onedrive.loadIDByPath(path.Dir(parent)); err != nil {
if !strings.Contains(err.Error(), "itemNotFound") {
return err
}
_ = o.createFolder(path.Dir(parent))
_ = onedrive.createFolder(path.Dir(parent))
}
item2, err := o.loadIDByPath(path.Dir(parent))
item2, err := onedrive.loadIDByPath(path.Dir(parent))
if err != nil {
return err
}
if _, err := o.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
if _, err := onedrive.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
return err
}
return nil
@@ -325,8 +307,8 @@ type DriveItem struct {
WebURL string `json:"webUrl"`
}
func (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
apiUrl, err := o.client.BaseURL.Parse(absoluteUrl)
func (onedrive *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
apiUrl, err := onedrive.client.BaseURL.Parse(absoluteUrl)
if err != nil {
return nil, err
}
+65 -50
View File
@@ -1,36 +1,54 @@
package client
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
osssdk "github.com/aliyun/aliyun-oss-go-sdk/oss"
)
type ossClient struct {
scType string
bucketStr string
client osssdk.Client
scType string
Vars map[string]interface{}
client osssdk.Client
}
func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
endpoint := loadParamFromVars("endpoint", true, vars)
accessKey := loadParamFromVars("accessKey", true, vars)
secretKey := loadParamFromVars("secretKey", true, vars)
bucketStr := loadParamFromVars("bucket", true, vars)
scType := loadParamFromVars("scType", true, vars)
if len(scType) == 0 {
var endpoint string
var accessKey string
var secretKey string
var scType string
if _, ok := vars["endpoint"]; ok {
endpoint = vars["endpoint"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["accessKey"]; ok {
accessKey = vars["accessKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["scType"]; ok {
scType = vars["scType"].(string)
} else {
scType = "Standard"
}
if _, ok := vars["secretKey"]; ok {
secretKey = vars["secretKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
client, err := osssdk.New(endpoint, accessKey, secretKey)
if err != nil {
return nil, err
}
return &ossClient{scType: scType, bucketStr: bucketStr, client: *client}, nil
return &ossClient{
scType: scType,
Vars: vars,
client: *client,
}, nil
}
func (o ossClient) ListBuckets() ([]interface{}, error) {
response, err := o.client.ListBuckets()
func (oss ossClient) ListBuckets() ([]interface{}, error) {
response, err := oss.client.ListBuckets()
if err != nil {
return nil, err
}
@@ -41,72 +59,69 @@ func (o ossClient) ListBuckets() ([]interface{}, error) {
return result, err
}
func (o ossClient) Exist(path string) (bool, error) {
bucket, err := o.client.Bucket(o.bucketStr)
func (oss ossClient) Exist(path string) (bool, error) {
bucket, err := oss.GetBucket()
if err != nil {
return false, err
}
return bucket.IsObjectExist(path)
}
func (o ossClient) Size(path string) (int64, error) {
bucket, err := o.client.Bucket(o.bucketStr)
if err != nil {
return 0, err
}
lor, err := bucket.ListObjectsV2(osssdk.Prefix(path))
if err != nil {
return 0, err
}
if len(lor.Objects) == 0 {
return 0, fmt.Errorf("no such file %s", path)
}
return lor.Objects[0].Size, nil
}
func (o ossClient) Delete(path string) (bool, error) {
bucket, err := o.client.Bucket(o.bucketStr)
func (oss ossClient) Delete(path string) (bool, error) {
bucket, err := oss.GetBucket()
if err != nil {
return false, err
}
if err := bucket.DeleteObject(path); err != nil {
err = bucket.DeleteObject(path)
if err != nil {
return false, err
}
return true, nil
}
func (o ossClient) Upload(src, target string) (bool, error) {
bucket, err := o.client.Bucket(o.bucketStr)
func (oss ossClient) Upload(src, target string) (bool, error) {
bucket, err := oss.GetBucket()
if err != nil {
return false, err
}
if err := bucket.UploadFile(target, src,
200*1024*1024,
osssdk.Routines(5),
osssdk.Checkpoint(true, ""),
osssdk.ObjectStorageClass(osssdk.StorageClassType(o.scType))); err != nil {
err = bucket.UploadFile(target, src, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""), osssdk.ObjectStorageClass(osssdk.StorageClassType(oss.scType)))
if err != nil {
return false, err
}
return true, nil
}
func (o ossClient) Download(src, target string) (bool, error) {
bucket, err := o.client.Bucket(o.bucketStr)
func (oss ossClient) Download(src, target string) (bool, error) {
bucket, err := oss.GetBucket()
if err != nil {
return false, err
}
if err := bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, "")); err != nil {
err = bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""))
if err != nil {
return false, err
}
return true, nil
}
func (o *ossClient) ListObjects(prefix string) ([]string, error) {
bucket, err := o.client.Bucket(o.bucketStr)
if err != nil {
return nil, err
func (oss *ossClient) GetBucket() (*osssdk.Bucket, error) {
if _, ok := oss.Vars["bucket"]; ok {
bucket, err := oss.client.Bucket(oss.Vars["bucket"].(string))
if err != nil {
return nil, err
}
return bucket, nil
} else {
return nil, constant.ErrInvalidParams
}
lor, err := bucket.ListObjectsV2(osssdk.Prefix(prefix))
}
func (oss *ossClient) ListObjects(prefix string) ([]string, error) {
bucket, err := oss.GetBucket()
if err != nil {
return nil, constant.ErrInvalidParams
}
lor, err := bucket.ListObjects(osssdk.Prefix(prefix))
if err != nil {
return nil, err
}
@@ -0,0 +1,75 @@
package client
import (
"encoding/json"
"fmt"
"os"
"testing"
"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/init/db"
"github.com/1Panel-dev/1Panel/backend/init/log"
"github.com/1Panel-dev/1Panel/backend/init/viper"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func TestCron(t *testing.T) {
viper.Init()
log.Init()
db.Init()
var backup model.BackupAccount
if err := global.DB.Where("id = ?", 2).First(&backup).Error; err != nil {
fmt.Println(err)
}
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
fmt.Println(err)
}
varMap["type"] = backup.Type
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo:
varMap["secretKey"] = backup.Credential
}
endpoint := varMap["endpoint"].(string)
accessKey := varMap["accessKey"].(string)
secretKey := varMap["secretKey"].(string)
client, err := oss.New(endpoint, accessKey, secretKey)
if err != nil {
fmt.Println(err)
}
bucket, err := client.Bucket(backup.Bucket)
if err != nil {
fmt.Println(err)
}
lor, err := bucket.ListObjects(oss.Prefix("directory/directory-test1/"))
if err != nil {
fmt.Println(err)
}
fmt.Println("my objects:", getObjectsFormResponse(lor))
name := "directory/directory-test1/20220928104331.tar.gz"
targetPath := constant.DataDir + "/download/directory/directory-test1/"
if _, err := os.Stat(targetPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetPath, os.ModePerm); err != nil {
fmt.Println(err)
}
}
if err := bucket.GetObjectToFile(name, targetPath+"20220928104231.tar.gz"); err != nil {
fmt.Println(err)
}
}
func getObjectsFormResponse(lor oss.ListObjectsResult) string {
var output string
for _, object := range lor.Objects {
output += object.Key + " "
}
return output
}
+107 -57
View File
@@ -3,6 +3,7 @@ package client
import (
"os"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
@@ -13,20 +14,41 @@ import (
type s3Client struct {
scType string
bucket string
Vars map[string]interface{}
Sess session.Session
}
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
accessKey := loadParamFromVars("accessKey", true, vars)
secretKey := loadParamFromVars("secretKey", true, vars)
endpoint := loadParamFromVars("endpoint", true, vars)
region := loadParamFromVars("region", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
scType := loadParamFromVars("scType", true, vars)
if len(scType) == 0 {
var accessKey string
var secretKey string
var endpoint string
var scType string
var region string
if _, ok := vars["accessKey"]; ok {
accessKey = vars["accessKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["secretKey"]; ok {
secretKey = vars["secretKey"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["scType"]; ok {
scType = vars["scType"].(string)
} else {
scType = "Standard"
}
if _, ok := vars["endpoint"]; ok {
endpoint = vars["endpoint"].(string)
} else {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["region"]; ok {
region = vars["region"].(string)
} else {
return nil, constant.ErrInvalidParams
}
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Endpoint: aws.String(endpoint),
@@ -37,12 +59,16 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
if err != nil {
return nil, err
}
return &s3Client{scType: scType, bucket: bucket, Sess: *sess}, nil
return &s3Client{
scType: scType,
Vars: vars,
Sess: *sess,
}, nil
}
func (s s3Client) ListBuckets() ([]interface{}, error) {
func (s3C s3Client) ListBuckets() ([]interface{}, error) {
var result []interface{}
svc := s3.New(&s.Sess)
svc := s3.New(&s3C.Sess)
res, err := svc.ListBuckets(nil)
if err != nil {
return nil, err
@@ -53,12 +79,17 @@ func (s s3Client) ListBuckets() ([]interface{}, error) {
return result, nil
}
func (s s3Client) Exist(path string) (bool, error) {
svc := s3.New(&s.Sess)
if _, err := svc.HeadObject(&s3.HeadObjectInput{
Bucket: &s.bucket,
func (s3C s3Client) Exist(path string) (bool, error) {
bucket, err := s3C.getBucket()
if err != nil {
return false, err
}
svc := s3.New(&s3C.Sess)
_, err = svc.HeadObject(&s3.HeadObjectInput{
Bucket: &bucket,
Key: &path,
}); err != nil {
})
if err != nil {
if aerr, ok := err.(awserr.RequestFailure); ok {
if aerr.StatusCode() == 404 {
return false, nil
@@ -70,53 +101,57 @@ func (s s3Client) Exist(path string) (bool, error) {
return true, nil
}
func (s *s3Client) Size(path string) (int64, error) {
svc := s3.New(&s.Sess)
file, err := svc.GetObject(&s3.GetObjectInput{
Bucket: &s.bucket,
Key: &path,
})
func (s3C s3Client) Delete(path string) (bool, error) {
bucket, err := s3C.getBucket()
if err != nil {
return 0, err
}
return *file.ContentLength, nil
}
func (s s3Client) Delete(path string) (bool, error) {
svc := s3.New(&s.Sess)
if _, err := svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(s.bucket), Key: aws.String(path)}); err != nil {
return false, err
}
if err := svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
Bucket: aws.String(s.bucket),
svc := s3.New(&s3C.Sess)
_, err = svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(path)})
if err != nil {
return false, err
}
err = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(path),
}); err != nil {
})
if err != nil {
return false, err
}
return true, nil
}
func (s s3Client) Upload(src, target string) (bool, error) {
func (s3C s3Client) Upload(src, target string) (bool, error) {
bucket, err := s3C.getBucket()
if err != nil {
return false, err
}
file, err := os.Open(src)
if err != nil {
return false, err
}
defer file.Close()
uploader := s3manager.NewUploader(&s.Sess)
if _, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket),
uploader := s3manager.NewUploader(&s3C.Sess)
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(bucket),
Key: aws.String(target),
Body: file,
StorageClass: &s.scType,
}); err != nil {
StorageClass: &s3C.scType,
})
if err != nil {
return false, err
}
return true, nil
}
func (s s3Client) Download(src, target string) (bool, error) {
if _, err := os.Stat(target); err != nil {
func (s3C s3Client) Download(src, target string) (bool, error) {
bucket, err := s3C.getBucket()
if err != nil {
return false, err
}
_, err = os.Stat(target)
if err != nil {
if os.IsNotExist(err) {
os.Remove(target)
} else {
@@ -128,29 +163,44 @@ func (s s3Client) Download(src, target string) (bool, error) {
return false, err
}
defer file.Close()
downloader := s3manager.NewDownloader(&s.Sess)
if _, err = downloader.Download(file, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(src),
}); err != nil {
downloader := s3manager.NewDownloader(&s3C.Sess)
_, err = downloader.Download(file,
&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(src),
})
if err != nil {
os.Remove(target)
return false, err
}
return true, nil
}
func (s *s3Client) ListObjects(prefix string) ([]string, error) {
svc := s3.New(&s.Sess)
var result []string
outputs, err := svc.ListObjects(&s3.ListObjectsInput{
Bucket: &s.bucket,
Prefix: &prefix,
})
if err != nil {
return result, err
func (s3C *s3Client) getBucket() (string, error) {
if _, ok := s3C.Vars["bucket"]; ok {
return s3C.Vars["bucket"].(string), nil
} else {
return "", constant.ErrInvalidParams
}
for _, item := range outputs.Contents {
result = append(result, *item.Key)
}
func (s3C *s3Client) ListObjects(prefix string) ([]string, error) {
bucket, err := s3C.getBucket()
if err != nil {
return nil, constant.ErrInvalidParams
}
svc := s3.New(&s3C.Sess)
var result []string
if err := svc.ListObjectsPages(&s3.ListObjectsInput{
Bucket: &bucket,
Prefix: &prefix,
}, func(p *s3.ListObjectsOutput, last bool) (shouldContinue bool) {
for _, obj := range p.Contents {
result = append(result, *obj.Key)
}
return true
}); err != nil {
return nil, err
}
return result, nil
}
+115 -78
View File
@@ -6,68 +6,71 @@ import (
"net"
"os"
"path"
"strconv"
"time"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
type sftpClient struct {
bucket string
connInfo string
config *ssh.ClientConfig
Vars map[string]interface{}
}
func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
address := loadParamFromVars("address", true, vars)
port := loadParamFromVars("port", false, vars)
password := loadParamFromVars("password", true, vars)
username := loadParamFromVars("username", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
auth := []ssh.AuthMethod{ssh.Password(password)}
clientConfig := &ssh.ClientConfig{
User: username,
Auth: auth,
Timeout: 30 * time.Second,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
if _, ok := vars["address"]; !ok {
return nil, constant.ErrInvalidParams
}
return &sftpClient{bucket: bucket, connInfo: fmt.Sprintf("%s:%s", address, port), config: clientConfig}, nil
if _, ok := vars["port"].(float64); !ok {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["password"]; !ok {
return nil, constant.ErrInvalidParams
}
if _, ok := vars["username"]; !ok {
return nil, constant.ErrInvalidParams
}
return &sftpClient{
Vars: vars,
}, nil
}
func (s sftpClient) Upload(src, target string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
bucket, err := s.getBucket()
if err != nil {
return false, err
}
client, err := sftp.NewClient(sshClient)
port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64))
if err != nil {
return false, err
}
defer client.Close()
defer sshClient.Close()
sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port)
if err != nil {
return false, err
}
defer sftpC.Close()
srcFile, err := os.Open(src)
if err != nil {
return false, err
}
defer srcFile.Close()
targetFilePath := s.bucket + "/" + target
targetDir, _ := path.Split(targetFilePath)
if _, err = client.Stat(targetDir); err != nil {
targetFilePath := bucket + "/" + target
remotePath, _ := path.Split(targetFilePath)
_, err = sftpC.Stat(remotePath)
if err != nil {
if os.IsNotExist(err) {
if err = client.MkdirAll(targetDir); err != nil {
err = sftpC.MkdirAll(remotePath)
if err != nil {
return false, err
}
} else {
return false, err
}
}
dstFile, err := client.Create(targetFilePath)
dstFile, err := sftpC.Create(targetFilePath)
if err != nil {
return false, err
}
@@ -93,17 +96,20 @@ func (s sftpClient) ListBuckets() ([]interface{}, error) {
}
func (s sftpClient) Download(src, target string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
bucket, err := s.getBucket()
if err != nil {
return false, err
}
client, err := sftp.NewClient(sshClient)
port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64))
if err != nil {
return false, err
}
defer client.Close()
defer sshClient.Close()
srcFile, err := client.Open(s.bucket + "/" + src)
sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port)
if err != nil {
return false, err
}
defer sftpC.Close()
srcFile, err := sftpC.Open(bucket + "/" + src)
if err != nil {
return false, err
}
@@ -122,18 +128,20 @@ func (s sftpClient) Download(src, target string) (bool, error) {
}
func (s sftpClient) Exist(path string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
bucket, err := s.getBucket()
if err != nil {
return false, err
}
client, err := sftp.NewClient(sshClient)
port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64))
if err != nil {
return false, err
}
defer client.Close()
defer sshClient.Close()
srcFile, err := client.Open(s.bucket + "/" + path)
sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port)
if err != nil {
return false, err
}
defer sftpC.Close()
srcFile, err := sftpC.Open(bucket + "/" + path)
if err != nil {
if os.IsNotExist(err) {
return false, nil
@@ -145,57 +153,86 @@ func (s sftpClient) Exist(path string) (bool, error) {
return true, err
}
func (s sftpClient) Size(path string) (int64, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return 0, err
}
client, err := sftp.NewClient(sshClient)
if err != nil {
return 0, err
}
defer client.Close()
defer sshClient.Close()
files, err := client.Stat(s.bucket + "/" + path)
if err != nil {
return 0, err
}
return files.Size(), nil
}
func (s sftpClient) Delete(filePath string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
bucket, err := s.getBucket()
if err != nil {
return false, err
}
client, err := sftp.NewClient(sshClient)
port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64))
if err != nil {
return false, err
}
defer client.Close()
defer sshClient.Close()
targetFilePath := s.bucket + "/" + filePath
if err := client.Remove(targetFilePath); err != nil {
sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port)
if err != nil {
return false, err
}
defer sftpC.Close()
targetFilePath := bucket + "/" + filePath
err = sftpC.Remove(targetFilePath)
if err != nil {
if os.IsNotExist(err) {
return true, nil
} else {
return false, err
}
}
return true, nil
}
func (s sftpClient) ListObjects(prefix string) ([]string, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return nil, err
}
client, err := sftp.NewClient(sshClient)
if err != nil {
return nil, err
}
defer client.Close()
defer sshClient.Close()
func connect(user, password, host string, port int) (*sftp.Client, error) {
files, err := client.ReadDir(s.bucket + "/" + prefix)
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
sshClient *ssh.Client
sftpClient *sftp.Client
err error
)
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(password))
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: 30 * time.Second,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr = fmt.Sprintf("%s:%d", host, port)
if sshClient, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return nil, err
}
if sftpClient, err = sftp.NewClient(sshClient); err != nil {
return nil, err
}
return sftpClient, nil
}
func (s sftpClient) getBucket() (string, error) {
if _, ok := s.Vars["bucket"]; ok {
return s.Vars["bucket"].(string), nil
} else {
return "", constant.ErrInvalidParams
}
}
func (s sftpClient) ListObjects(prefix string) ([]string, error) {
bucket, err := s.getBucket()
if err != nil {
return nil, err
}
port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64))
if err != nil {
return nil, err
}
sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port)
if err != nil {
return nil, err
}
defer sftpC.Close()
files, err := sftpC.ReadDir(bucket + "/" + prefix)
if err != nil {
return nil, err
}
@@ -0,0 +1,44 @@
package client
import (
"encoding/json"
"fmt"
"testing"
"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/init/db"
"github.com/1Panel-dev/1Panel/backend/init/log"
"github.com/1Panel-dev/1Panel/backend/init/viper"
)
func TestCronS(t *testing.T) {
viper.Init()
log.Init()
db.Init()
var backup model.BackupAccount
if err := global.DB.Where("id = ?", 5).First(&backup).Error; err != nil {
fmt.Println(err)
}
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
fmt.Println(err)
}
varMap["type"] = backup.Type
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo:
varMap["secretKey"] = backup.Credential
}
client, err := NewS3Client(varMap)
if err != nil {
fmt.Println(err)
}
_, _ = client.ListObjects("directory/directory-test-s3")
}
@@ -1,117 +0,0 @@
package client
import (
"fmt"
"io"
"os"
"github.com/studio-b12/gowebdav"
)
type webDAVClient struct {
Bucket string
client *gowebdav.Client
}
func NewWebDAVClient(vars map[string]interface{}) (*webDAVClient, error) {
address := loadParamFromVars("address", true, vars)
port := loadParamFromVars("port", false, vars)
password := loadParamFromVars("password", true, vars)
username := loadParamFromVars("username", true, vars)
bucket := loadParamFromVars("bucket", true, vars)
client := gowebdav.NewClient(fmt.Sprintf("%s:%s", address, port), username, password)
if err := client.Connect(); err != nil {
return nil, err
}
return &webDAVClient{Bucket: bucket, client: client}, nil
}
func (s webDAVClient) Upload(src, target string) (bool, error) {
targetFilePath := s.Bucket + "/" + target
fileInfo, err := os.Stat(src)
if err != nil {
return false, err
}
// 50M
if fileInfo.Size() > 52428800 {
bytes, _ := os.ReadFile(src)
if err := s.client.Write(targetFilePath, bytes, 0644); err != nil {
return false, err
}
return true, nil
}
file, _ := os.Open(src)
defer file.Close()
if err := s.client.WriteStream(targetFilePath, file, 0644); err != nil {
return false, err
}
return true, nil
}
func (s webDAVClient) ListBuckets() ([]interface{}, error) {
var result []interface{}
return result, nil
}
func (s webDAVClient) Download(src, target string) (bool, error) {
srcPath := s.Bucket + "/" + src
info, err := s.client.Stat(srcPath)
if err != nil {
return false, err
}
file, err := os.Create(target)
if err != nil {
return false, err
}
defer file.Close()
// 50M
if info.Size() > 52428800 {
reader, _ := s.client.ReadStream(srcPath)
if _, err := io.Copy(file, reader); err != nil {
return false, err
}
}
bytes, _ := s.client.Read(srcPath)
if err := os.WriteFile(target, bytes, 0644); err != nil {
return false, err
}
return true, err
}
func (s webDAVClient) Exist(path string) (bool, error) {
if _, err := s.client.Stat(s.Bucket + "/" + path); err != nil {
return false, err
}
return true, nil
}
func (s webDAVClient) Size(path string) (int64, error) {
file, err := s.client.Stat(s.Bucket + "/" + path)
if err != nil {
return 0, err
}
return file.Size(), nil
}
func (s webDAVClient) Delete(filePath string) (bool, error) {
if err := s.client.Remove(s.Bucket + "/" + filePath); err != nil {
return false, err
}
return true, nil
}
func (s webDAVClient) ListObjects(prefix string) ([]string, error) {
files, err := s.client.ReadDir(s.Bucket + "/" + prefix)
if err != nil {
return nil, err
}
var result []string
for _, file := range files {
result = append(result, file.Name())
}
return result, nil
}

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