mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b93ea93cfe | ||
|
|
91a895ea0c | ||
|
|
2cf950b49e | ||
|
|
d8f0070b3a | ||
|
|
b4d64bf2a6 | ||
|
|
cfc48733f0 | ||
|
|
230d04dd75 | ||
|
|
ed6735e610 | ||
|
|
168b6b8667 | ||
|
|
745d87a6d3 | ||
|
|
9bfc159e9e | ||
|
|
72d8825db0 | ||
|
|
d567491770 | ||
|
|
d60e8167b0 | ||
|
|
75ac8bae4d | ||
|
|
0931395c09 | ||
|
|
94a2d4b475 | ||
|
|
e48c224c41 | ||
|
|
a2c16e6a54 | ||
|
|
998c870566 | ||
|
|
b89b529252 | ||
|
|
88814e3d74 | ||
|
|
3a6e526033 | ||
|
|
73d41675a0 | ||
|
|
2aea2d3dfe | ||
|
|
fcba07d37a | ||
|
|
fc5070e330 | ||
|
|
5f60ea3f81 | ||
|
|
0fe9a87ea0 | ||
|
|
3f8abf8ad8 | ||
|
|
d4def44545 | ||
|
|
fb709e7eaf | ||
|
|
504d5f8596 | ||
|
|
bd2003c1b6 | ||
|
|
b8d3ab4f61 | ||
|
|
011ef0818d | ||
|
|
48ed9d8993 | ||
|
|
a6e12d88a3 | ||
|
|
40da8a7525 | ||
|
|
cb6b9965ec | ||
|
|
02f41ba45d | ||
|
|
2fdca95255 | ||
|
|
b6cbca36c2 | ||
|
|
a463026efe | ||
|
|
8a24342b85 | ||
|
|
a74ed6a317 | ||
|
|
b1e4ad9bfc | ||
|
|
d79814ff05 | ||
|
|
592a1b1150 | ||
|
|
5f9073cb85 | ||
|
|
1a400e7f81 | ||
|
|
58b6a2de32 | ||
|
|
14142be38f | ||
|
|
30c577dbaf | ||
|
|
282c58ca86 | ||
|
|
00ffac7210 | ||
|
|
c47c8b0f36 | ||
|
|
0a761e9e79 | ||
|
|
86d5a68cd6 | ||
|
|
a09fd97b10 | ||
|
|
10aff32bb1 | ||
|
|
d087811ae3 | ||
|
|
d2a01fb0e0 | ||
|
|
cceed9c2ea | ||
|
|
fd4c3f2f2e | ||
|
|
d60338f350 | ||
|
|
9658ebdfdb | ||
|
|
762a572c40 | ||
|
|
bcc89f4d99 | ||
|
|
64ad829e3c | ||
|
|
4daed1a3de | ||
|
|
246115837e | ||
|
|
e773bcbda2 | ||
|
|
e687f82126 | ||
|
|
732080b0bf | ||
|
|
833efb0136 | ||
|
|
ca45edae5f | ||
|
|
7c3c4db81b | ||
|
|
ed89f8fdd7 | ||
|
|
fa9d855523 | ||
|
|
82881273ea | ||
|
|
4ed131cde7 | ||
|
|
120bec57b2 | ||
|
|
6db80141ed | ||
|
|
08988c7db6 | ||
|
|
22358e161f | ||
|
|
10848fb249 | ||
|
|
86bc75ff28 | ||
|
|
59c216c184 | ||
|
|
73b2804d68 | ||
|
|
e41661e8d4 | ||
|
|
807a5071a7 | ||
|
|
50b4458926 | ||
|
|
a90b79bf76 | ||
|
|
1df4a870a0 | ||
|
|
fdb6242d03 | ||
|
|
9cd7f0d681 | ||
|
|
bb56231055 | ||
|
|
b5592327dd | ||
|
|
038879819d | ||
|
|
2c612a5919 | ||
|
|
fe22a005df | ||
|
|
a46d29cc37 | ||
|
|
5127fec4b3 | ||
|
|
5e1ad4b3a1 | ||
|
|
0ad5fe292b | ||
|
|
a0e1c30e22 | ||
|
|
7972de5f33 | ||
|
|
a5a707b923 | ||
|
|
055216604e | ||
|
|
52b16f0cb4 | ||
|
|
cda900e04a | ||
|
|
c260b858bf | ||
|
|
6c7e9d0a52 | ||
|
|
8f05668bcc | ||
|
|
8034b14cda | ||
|
|
824dba2a9f | ||
|
|
7e6c39bb08 | ||
|
|
adde751b73 | ||
|
|
88c3559245 | ||
|
|
49d8582658 | ||
|
|
d254837c12 | ||
|
|
d044cf2c2c | ||
|
|
abaa42af88 | ||
|
|
dc51148a29 | ||
|
|
6301fede62 | ||
|
|
ff6ede9429 | ||
|
|
9d637c0397 | ||
|
|
ea3b5356be |
@@ -44,6 +44,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
- [在线文档](https://1panel.cn/docs/)
|
||||
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
|
||||
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
|
||||
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
|
||||
|
||||
## 安全说明
|
||||
|
||||
|
||||
+1
-1
@@ -16,6 +16,6 @@ All security bugs should be reported to the contact as below:
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
- +86 400-052-0755
|
||||
|
||||
Thanks for your support!
|
||||
|
||||
@@ -6,7 +6,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -72,10 +71,6 @@ 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)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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"
|
||||
@@ -21,6 +23,14 @@ 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)
|
||||
@@ -43,6 +53,14 @@ 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))
|
||||
}
|
||||
@@ -173,6 +191,14 @@ 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)
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
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 [get]
|
||||
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)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
|
||||
// @Param request body dto.LogOption true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/daemonjson/update [post]
|
||||
// @Router /containers/logoption/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,6 +99,29 @@ 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 配置文件
|
||||
|
||||
@@ -33,6 +33,9 @@ var (
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
|
||||
deviceService = service.NewIDeviceService()
|
||||
fail2banService = service.NewIFail2BanService()
|
||||
|
||||
settingService = service.NewISettingService()
|
||||
backupService = service.NewIBackupService()
|
||||
|
||||
@@ -56,4 +59,6 @@ var (
|
||||
|
||||
recycleBinService = service.NewIRecycleBinService()
|
||||
favoriteService = service.NewIFavoriteService()
|
||||
|
||||
websiteCAService = service.NewIWebsiteCAService()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
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)
|
||||
}
|
||||
@@ -685,25 +685,21 @@ 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/read [post]
|
||||
// @Router /files/log/read [post]
|
||||
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
||||
var req request.FileReadByLineReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
lines, end, err := files.ReadFileByLine(req.Path, req.Page, req.PageSize)
|
||||
res, err := fileService.ReadLogByLine(req)
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,3 +68,19 @@ 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)
|
||||
}
|
||||
|
||||
@@ -46,11 +46,12 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := runtimeService.Create(req); err != nil {
|
||||
ssl, err := runtimeService.Create(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
helper.SuccessWithData(c, ssl)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
|
||||
@@ -217,43 +217,6 @@ 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 获取安装根目录
|
||||
@@ -288,37 +251,6 @@ 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 信息
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
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"
|
||||
@@ -66,24 +69,24 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Website SSL
|
||||
// @Summary Reset website ssl
|
||||
// @Description 重置网站 ssl
|
||||
// @Summary Apply ssl
|
||||
// @Description 申请证书
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteSSLRenew true "request"
|
||||
// @Param request body request.WebsiteSSLApply true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @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
|
||||
// @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
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteSSLService.Renew(req.SSLID); err != nil {
|
||||
if err := websiteSSLService.ObtainSSL(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website SSL
|
||||
@@ -111,17 +114,17 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||
// @Summary Delete website ssl
|
||||
// @Description 删除网站 ssl
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteResourceReq true "request"
|
||||
// @Param request body request.WebsiteBatchDelReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/del [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":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
|
||||
// @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]"}
|
||||
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
var req request.WebsiteBatchDelReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteSSLService.Delete(req.ID); err != nil {
|
||||
if err := websiteSSLService.Delete(req.IDs); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@@ -213,3 +216,32 @@ 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)
|
||||
}
|
||||
|
||||
@@ -13,12 +13,18 @@ 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"`
|
||||
|
||||
@@ -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"`
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
@@ -52,10 +52,11 @@ 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"`
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
|
||||
FileDir string `json:"fileDir" validate:"required"`
|
||||
FileName string `json:"fileName" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -41,6 +41,15 @@ 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"`
|
||||
|
||||
@@ -52,6 +52,7 @@ 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"`
|
||||
@@ -135,13 +136,21 @@ 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"`
|
||||
Subnet string `json:"subnet"`
|
||||
Gateway string `json:"gateway"`
|
||||
IPRange string `json:"ipRange"`
|
||||
Labels []string `json:"labels"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type Volume struct {
|
||||
@@ -159,6 +168,7 @@ type VolumeCreate struct {
|
||||
}
|
||||
|
||||
type BatchDelete struct {
|
||||
Force bool `json:"force"`
|
||||
Names []string `json:"names" validate:"required"`
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,12 @@ type DashboardCurrent struct {
|
||||
MemoryTotal uint64 `json:"memoryTotal"`
|
||||
MemoryAvailable uint64 `json:"memoryAvailable"`
|
||||
MemoryUsed uint64 `json:"memoryUsed"`
|
||||
MemoryUsedPercent float64 `json:"MemoryUsedPercent"`
|
||||
MemoryUsedPercent float64 `json:"memoryUsedPercent"`
|
||||
|
||||
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
|
||||
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
|
||||
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
|
||||
SwapMemoryUsedPercent float64 `json:"swapMemoryUsedPercent"`
|
||||
|
||||
IOReadBytes uint64 `json:"ioReadBytes"`
|
||||
IOWriteBytes uint64 `json:"ioWriteBytes"`
|
||||
|
||||
+47
-26
@@ -227,17 +227,24 @@ 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"`
|
||||
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"`
|
||||
|
||||
SSL bool `json:"ssl"`
|
||||
RootCert string `json:"rootCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
SkipVerify bool `json:"skipVerify"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type DatabaseOption struct {
|
||||
@@ -250,25 +257,39 @@ 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"`
|
||||
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"`
|
||||
|
||||
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"`
|
||||
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"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
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"`
|
||||
}
|
||||
@@ -14,6 +14,11 @@ 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"`
|
||||
}
|
||||
@@ -23,6 +28,12 @@ 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"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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"`
|
||||
}
|
||||
@@ -118,9 +118,11 @@ 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 {
|
||||
|
||||
@@ -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"`
|
||||
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 WebsiteCommonReq struct {
|
||||
|
||||
@@ -14,6 +14,12 @@ 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 {
|
||||
@@ -25,8 +31,16 @@ 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type WebsiteDnsAccountCreate struct {
|
||||
@@ -46,9 +60,14 @@ 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"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
AutoRenew bool `json:"autoRenew"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type WebsiteSSLUpload struct {
|
||||
@@ -57,4 +76,39 @@ 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"`
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
|
||||
type WebsiteSSLDTO struct {
|
||||
model.WebsiteSSL
|
||||
LogPath string `json:"logPath"`
|
||||
}
|
||||
|
||||
type WebsiteDNSRes struct {
|
||||
@@ -21,3 +22,13 @@ 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"`
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package dto
|
||||
import "time"
|
||||
|
||||
type SettingInfo struct {
|
||||
UserName string `json:"userName"`
|
||||
Email string `json:"email"`
|
||||
SystemIP string `json:"systemIP"`
|
||||
SystemVersion string `json:"systemVersion"`
|
||||
UserName string `json:"userName"`
|
||||
Email string `json:"email"`
|
||||
SystemIP string `json:"systemIP"`
|
||||
SystemVersion string `json:"systemVersion"`
|
||||
DockerSockPath string `json:"dockerSockPath"`
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
LocalTime string `json:"localTime"`
|
||||
@@ -49,6 +50,8 @@ type SettingInfo struct {
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||
|
||||
FileRecycleBin string `json:"fileRecycleBin"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -57,7 +60,7 @@ type SettingUpdate struct {
|
||||
}
|
||||
|
||||
type SSLUpdate struct {
|
||||
SSLType string `json:"sslType"`
|
||||
SSLType string `json:"sslType" validate:"required,oneof=self select import-paste import-local"`
|
||||
Domain string `json:"domain"`
|
||||
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
|
||||
Cert string `json:"cert"`
|
||||
@@ -97,7 +100,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"`
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive WebDAV"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
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"`
|
||||
@@ -24,3 +30,14 @@ 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)
|
||||
}
|
||||
|
||||
@@ -11,5 +11,12 @@ 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)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
|
||||
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);"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ package model
|
||||
|
||||
type WebsiteAcmeAccount struct {
|
||||
BaseModel
|
||||
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:"-"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccount) TableName() string {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
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,6 +1,11 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebsiteSSL struct {
|
||||
BaseModel
|
||||
@@ -14,14 +19,26 @@ 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))
|
||||
}
|
||||
|
||||
@@ -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("WebsiteSSL").Find(&websites).Error
|
||||
err := getDb(opts...).Model(&model.Website{}).Preload("Domains").Preload("WebsiteSSL").Find(&websites).Error
|
||||
return websites, err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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
|
||||
}
|
||||
@@ -14,11 +14,12 @@ 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
|
||||
}
|
||||
|
||||
@@ -43,19 +44,25 @@ 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("Websites").Find(&sslList).Error
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("DnsAccount").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").First(&website).Error; err != nil {
|
||||
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
|
||||
return website, err
|
||||
}
|
||||
return website, nil
|
||||
@@ -64,7 +71,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").Find(&websites).Error; err != nil {
|
||||
if err := db.Preload("AcmeAccount").Preload("DnsAccount").Find(&websites).Error; err != nil {
|
||||
return websites, err
|
||||
}
|
||||
return websites, nil
|
||||
@@ -74,7 +81,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
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -8,7 +8,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"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 != "" {
|
||||
if req.Resource != "" && req.Resource != "all" {
|
||||
opts = append(opts, appRepo.WithResource(req.Resource))
|
||||
}
|
||||
if len(req.Tags) != 0 {
|
||||
@@ -173,7 +173,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
return appDetailDTO, err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
|
||||
|
||||
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
|
||||
if !fileOp.Stat(versionPath) || detail.Update {
|
||||
if err = downloadApp(app, detail, nil); err != nil {
|
||||
return appDetailDTO, err
|
||||
@@ -181,8 +182,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
}
|
||||
switch app.Type {
|
||||
case constant.RuntimePHP:
|
||||
buildPath := path.Join(versionPath, "build")
|
||||
paramsPath := path.Join(buildPath, "config.json")
|
||||
buildPath := filepath.Join(versionPath, "build")
|
||||
paramsPath := filepath.Join(buildPath, "config.json")
|
||||
if !fileOp.Stat(paramsPath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
}
|
||||
@@ -195,7 +196,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
return appDetailDTO, err
|
||||
}
|
||||
appDetailDTO.Params = paramMap
|
||||
composePath := path.Join(buildPath, "docker-compose.yml")
|
||||
composePath := filepath.Join(buildPath, "docker-compose.yml")
|
||||
if !fileOp.Stat(composePath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
}
|
||||
@@ -223,7 +224,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
}
|
||||
|
||||
if appDetailDTO.DockerCompose == "" {
|
||||
filename := path.Base(appDetailDTO.DownloadUrl)
|
||||
filename := filepath.Base(appDetailDTO.DownloadUrl)
|
||||
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
if err != nil {
|
||||
@@ -489,7 +490,7 @@ func (a AppService) SyncAppListFromLocal() {
|
||||
}
|
||||
for _, dirEntry := range dirEntries {
|
||||
if dirEntry.IsDir() {
|
||||
appDir := path.Join(localAppDir, dirEntry.Name())
|
||||
appDir := filepath.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()}))
|
||||
@@ -507,7 +508,7 @@ func (a AppService) SyncAppListFromLocal() {
|
||||
Version: appDirEntry.Name(),
|
||||
Status: constant.AppNormal,
|
||||
}
|
||||
versionDir := path.Join(appDir, appDirEntry.Name())
|
||||
versionDir := filepath.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
|
||||
@@ -742,7 +743,7 @@ func getAppFromRepo(downloadPath string) error {
|
||||
downloadUrl := downloadPath
|
||||
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
|
||||
fileOp := files.NewFileOp()
|
||||
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
|
||||
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
|
||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -760,7 +761,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 := path.Join(constant.ResourceDir, "1panel.json")
|
||||
listFile := filepath.Join(constant.ResourceDir, "1panel.json")
|
||||
content, err := os.ReadFile(listFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -261,6 +262,8 @@ 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")
|
||||
}
|
||||
@@ -505,6 +508,9 @@ 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
|
||||
}
|
||||
|
||||
|
||||
@@ -118,9 +118,13 @@ 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 app.Type == "runtime" && DatabaseKeys[app.Key] > 0 {
|
||||
if DatabaseKeys[app.Key] > 0 {
|
||||
database := &model.Database{
|
||||
AppInstallID: appInstall.ID,
|
||||
Name: appInstall.Name,
|
||||
@@ -153,14 +157,6 @@ 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"
|
||||
@@ -168,6 +164,16 @@ 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":
|
||||
@@ -189,6 +195,23 @@ 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 {
|
||||
@@ -336,7 +359,6 @@ 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
|
||||
@@ -383,33 +405,66 @@ 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")
|
||||
@@ -419,16 +474,6 @@ 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
|
||||
@@ -481,24 +526,6 @@ 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)
|
||||
@@ -560,26 +587,6 @@ 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)
|
||||
@@ -630,8 +637,12 @@ 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 := path.Join(appResourceDir, app.Key)
|
||||
appDownloadDir := app.GetAppResourcePath()
|
||||
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
|
||||
fileOp := files.NewFileOp()
|
||||
if !appDetail.Update && fileOp.Stat(appVersionDir) {
|
||||
@@ -897,28 +908,28 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
|
||||
fileOp := files.NewFileOp()
|
||||
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
|
||||
if !fileOp.Stat(dockerComposePath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
return buserr.WithName(constant.ErrFileNotFound, "docker-compose.yml")
|
||||
}
|
||||
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
|
||||
if dockerComposeByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
|
||||
return buserr.WithName(constant.ErrFileParseApp, "docker-compose.yml")
|
||||
}
|
||||
appDetail.DockerCompose = string(dockerComposeByte)
|
||||
paramPath := path.Join(versionDir, "data.yml")
|
||||
if !fileOp.Stat(paramPath) {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||
}
|
||||
paramByte, _ := fileOp.GetContent(paramPath)
|
||||
if paramByte == nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||
}
|
||||
appParamConfig := dto.LocalAppParam{}
|
||||
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
||||
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
}
|
||||
dataJson, err := json.Marshal(appParamConfig.AppParams)
|
||||
if err != nil {
|
||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
}
|
||||
appDetail.Params = string(dataJson)
|
||||
return nil
|
||||
@@ -928,22 +939,22 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
configYamlPath := path.Join(appDir, "data.yml")
|
||||
if !fileOp.Stat(configYamlPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
||||
err = buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||
return
|
||||
}
|
||||
iconPath := path.Join(appDir, "logo.png")
|
||||
if !fileOp.Stat(iconPath) {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
|
||||
err = buserr.WithName(constant.ErrFileNotFound, "logo.png")
|
||||
return
|
||||
}
|
||||
configYamlByte, err := fileOp.GetContent(configYamlPath)
|
||||
if err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
return
|
||||
}
|
||||
localAppDefine := dto.LocalAppAppDefine{}
|
||||
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
|
||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
||||
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
return
|
||||
}
|
||||
app = &localAppDefine.AppProperty
|
||||
@@ -1152,7 +1163,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
if portExist {
|
||||
allowHost := "127.0.0.1"
|
||||
if req.Advanced && req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
allowHost = ""
|
||||
}
|
||||
params[constant.HostIP] = allowHost
|
||||
}
|
||||
@@ -1164,7 +1175,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig
|
||||
config := request.AppContainerConfig{}
|
||||
|
||||
if hostIp, ok := envs[constant.HostIP]; ok {
|
||||
config.AllowPort = hostIp.(string) == "0.0.0.0"
|
||||
config.AllowPort = hostIp.(string) != "127.0.0.1"
|
||||
} else {
|
||||
config.AllowPort = true
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ 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
|
||||
}
|
||||
|
||||
@@ -80,15 +81,46 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
||||
commonRepo.WithByType(search.Type),
|
||||
backupRepo.WithByDetailName(search.DetailName),
|
||||
)
|
||||
var dtobas []dto.BackupRecords
|
||||
for _, group := range records {
|
||||
|
||||
var datas []dto.BackupRecords
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &group); err != nil {
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtobas = append(dtobas, item)
|
||||
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)
|
||||
}
|
||||
return total, dtobas, err
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
type loadSizeHelper struct {
|
||||
backupPath string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadOneDriveInfo() (string, error) {
|
||||
@@ -117,7 +149,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
@@ -138,9 +170,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
}
|
||||
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
srcPath = itemPath + srcPath
|
||||
srcPath = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), srcPath)
|
||||
}
|
||||
if exist, _ := backClient.Exist(srcPath); exist {
|
||||
isOK, err := backClient.Download(srcPath, targetPath)
|
||||
@@ -166,8 +196,7 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
}
|
||||
}
|
||||
if req.Type != "LOCAL" {
|
||||
isOk, err := u.checkBackupConn(&backup)
|
||||
if err != nil || !isOk {
|
||||
if _, err := u.checkBackupConn(&backup); err != nil {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
}
|
||||
@@ -183,7 +212,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
return nil, err
|
||||
}
|
||||
switch backupDto.Type {
|
||||
case constant.Sftp:
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
varMap["username"] = backupDto.AccessKey
|
||||
varMap["password"] = backupDto.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
@@ -328,7 +357,7 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -160,7 +159,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
ports := simplifyPort(item.Ports)
|
||||
ports := loadContainerPort(item.Ports)
|
||||
backDatas[i] = dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
@@ -408,6 +407,7 @@ 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 {
|
||||
@@ -441,7 +441,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
|
||||
}
|
||||
}
|
||||
config := oldContainer.Config
|
||||
@@ -699,18 +699,15 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
|
||||
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
|
||||
filePath := ""
|
||||
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 req.Type == "compose-detail" {
|
||||
cli, 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 := client.ContainerList(context.Background(), options)
|
||||
containers, err := cli.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
@@ -725,9 +722,6 @@ 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 ""
|
||||
@@ -937,6 +931,7 @@ 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
|
||||
@@ -1018,6 +1013,22 @@ 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 {
|
||||
@@ -1048,13 +1059,19 @@ 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)
|
||||
}
|
||||
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
|
||||
if len(start.IP) == 0 {
|
||||
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
|
||||
}
|
||||
datas = append(datas, 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)
|
||||
}
|
||||
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
|
||||
if len(start.IP) == 0 {
|
||||
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
|
||||
}
|
||||
datas = append(datas, itemPortStr)
|
||||
}
|
||||
start = ports[i]
|
||||
}
|
||||
@@ -1064,13 +1081,19 @@ 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)
|
||||
}
|
||||
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
|
||||
if len(start.IP) == 0 {
|
||||
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
|
||||
}
|
||||
datas = append(datas, 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)
|
||||
}
|
||||
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
|
||||
if len(start.IP) == 0 {
|
||||
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
|
||||
}
|
||||
datas = append(datas, itemPortStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,8 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
if cmd.CheckIllegal(req.Name, req.Path) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
oldFile, err := os.ReadFile(req.Path)
|
||||
if 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)
|
||||
@@ -230,10 +231,16 @@ 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 {
|
||||
return errors.New(string(stdout))
|
||||
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))
|
||||
}
|
||||
if stdout, err := compose.Up(req.Path); err != nil {
|
||||
return errors.New(string(stdout))
|
||||
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 nil
|
||||
@@ -261,3 +268,19 @@ 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
|
||||
}
|
||||
|
||||
@@ -116,29 +116,57 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
ipam network.IPAMConfig
|
||||
hasConf bool
|
||||
ipams []network.IPAMConfig
|
||||
enableV6 bool
|
||||
)
|
||||
if len(req.Subnet) != 0 {
|
||||
ipam.Subnet = req.Subnet
|
||||
hasConf = true
|
||||
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.Gateway) != 0 {
|
||||
ipam.Gateway = req.Gateway
|
||||
hasConf = true
|
||||
}
|
||||
if len(req.IPRange) != 0 {
|
||||
ipam.IPRange = req.IPRange
|
||||
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)
|
||||
}
|
||||
|
||||
options := types.NetworkCreate{
|
||||
Driver: req.Driver,
|
||||
Options: stringsToMap(req.Options),
|
||||
Labels: stringsToMap(req.Labels),
|
||||
EnableIPv6: enableV6,
|
||||
Driver: req.Driver,
|
||||
Options: stringsToMap(req.Options),
|
||||
Labels: stringsToMap(req.Labels),
|
||||
}
|
||||
if hasConf {
|
||||
options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}}
|
||||
if len(ipams) != 0 {
|
||||
options.IPAM = &network.IPAM{Config: ipams}
|
||||
}
|
||||
if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil {
|
||||
return err
|
||||
|
||||
@@ -9,6 +9,7 @@ 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"
|
||||
@@ -51,15 +52,20 @@ 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)
|
||||
}
|
||||
if len(item.CreatedAt) > 19 {
|
||||
item.CreatedAt = item.CreatedAt[0:19]
|
||||
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)
|
||||
}
|
||||
createTime, _ := time.Parse("2006-01-02T15:04:05", item.CreatedAt)
|
||||
data = append(data, dto.Volume{
|
||||
CreatedAt: createTime,
|
||||
Name: item.Name,
|
||||
|
||||
@@ -157,9 +157,7 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
|
||||
return "", err
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + itemFileDir
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return "", err
|
||||
@@ -193,9 +191,7 @@ func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string,
|
||||
fileItem := file
|
||||
if cronjob.KeepLocal {
|
||||
if len(backupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
fileItem = itemPath + strings.TrimPrefix(file, localDir+"/")
|
||||
fileItem = path.Join(strings.TrimPrefix(backupPath, "/") + strings.TrimPrefix(file, localDir+"/"))
|
||||
} else {
|
||||
fileItem = strings.TrimPrefix(file, localDir+"/")
|
||||
}
|
||||
@@ -332,9 +328,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + itemFileDir
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
@@ -475,9 +469,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAcc
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + itemFileDir
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
@@ -551,9 +543,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
||||
}()
|
||||
}
|
||||
if len(backup.BackupPath) != 0 {
|
||||
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
|
||||
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
|
||||
itemFileDir = itemPath + itemFileDir
|
||||
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
@@ -594,7 +584,7 @@ func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.T
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemClean() (string, error) {
|
||||
return NewISettingService().SystemCleanForCronjob()
|
||||
return NewIDeviceService().CleanForCronjob()
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
|
||||
|
||||
@@ -3,7 +3,9 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -123,6 +125,12 @@ 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" {
|
||||
@@ -173,7 +181,7 @@ type diskInfo struct {
|
||||
|
||||
func loadDiskInfo() []dto.DiskInfo {
|
||||
var datas []dto.DiskInfo
|
||||
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
|
||||
stdout, err := cmd.ExecWithTimeOut("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 2*time.Second)
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
@@ -207,24 +215,52 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
)
|
||||
wg.Add(len(mounts))
|
||||
for i := 0; i < len(mounts); i++ {
|
||||
state, err := disk.Usage(mounts[i].Mount)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var itemData dto.DiskInfo
|
||||
itemData.Path = mounts[i].Mount
|
||||
itemData.Type = mounts[i].Type
|
||||
itemData.Device = mounts[i].Device
|
||||
itemData.Total = state.Total
|
||||
itemData.Free = state.Free
|
||||
itemData.Used = state.Used
|
||||
itemData.UsedPercent = state.UsedPercent
|
||||
itemData.InodesTotal = state.InodesTotal
|
||||
itemData.InodesUsed = state.InodesUsed
|
||||
itemData.InodesFree = state.InodesFree
|
||||
itemData.InodesUsedPercent = state.InodesUsedPercent
|
||||
datas = append(datas, itemData)
|
||||
go func(timeoutCh <-chan time.Time, mount diskInfo) {
|
||||
defer wg.Done()
|
||||
|
||||
var itemData dto.DiskInfo
|
||||
itemData.Path = mount.Mount
|
||||
itemData.Type = mount.Type
|
||||
itemData.Device = mount.Device
|
||||
select {
|
||||
case <-timeoutCh:
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
|
||||
default:
|
||||
state, err := disk.Usage(mount.Mount)
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
|
||||
return
|
||||
}
|
||||
itemData.Total = state.Total
|
||||
itemData.Free = state.Free
|
||||
itemData.Used = state.Used
|
||||
itemData.UsedPercent = state.UsedPercent
|
||||
itemData.InodesTotal = state.InodesTotal
|
||||
itemData.InodesUsed = state.InodesUsed
|
||||
itemData.InodesFree = state.InodesFree
|
||||
itemData.InodesUsedPercent = state.InodesUsedPercent
|
||||
mu.Lock()
|
||||
datas = append(datas, itemData)
|
||||
mu.Unlock()
|
||||
}
|
||||
}(time.After(5*time.Second), mounts[i])
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].Path < datas[j].Path
|
||||
})
|
||||
return datas
|
||||
}
|
||||
|
||||
@@ -84,7 +84,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -105,7 +111,13 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 6,
|
||||
|
||||
SSL: req.SSL,
|
||||
RootCert: req.RootCert,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
Timeout: 6,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,7 +184,14 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
|
||||
SSL: req.SSL,
|
||||
ClientKey: req.ClientKey,
|
||||
ClientCert: req.ClientCert,
|
||||
RootCert: req.RootCert,
|
||||
SkipVerify: req.SkipVerify,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -189,6 +208,10 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = pass
|
||||
upMap["description"] = req.Description
|
||||
upMap["ssl"] = req.SSL
|
||||
upMap["client_key"] = req.ClientKey
|
||||
upMap["client_cert"] = req.ClientCert
|
||||
upMap["root_cert"] = req.RootCert
|
||||
upMap["skip_verify"] = req.SkipVerify
|
||||
return databaseRepo.Update(req.ID, upMap)
|
||||
}
|
||||
|
||||
@@ -641,6 +641,11 @@ 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 {
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
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, _ := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
baseInfo.Ntp = ntp.Value
|
||||
|
||||
swapInfo, _ := mem.SwapMemory()
|
||||
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 --show --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
|
||||
}
|
||||
@@ -33,7 +33,7 @@ const (
|
||||
taskPath = "1panel/task"
|
||||
)
|
||||
|
||||
func (u *SettingService) SystemScan() dto.CleanData {
|
||||
func (u *DeviceService) Scan() dto.CleanData {
|
||||
var (
|
||||
SystemClean dto.CleanData
|
||||
treeData []dto.CleanTree
|
||||
@@ -133,7 +133,7 @@ func (u *SettingService) SystemScan() dto.CleanData {
|
||||
return SystemClean
|
||||
}
|
||||
|
||||
func (u *SettingService) SystemClean(req []dto.Clean) {
|
||||
func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
size := uint64(0)
|
||||
restart := false
|
||||
for _, item := range req {
|
||||
@@ -235,7 +235,7 @@ func (u *SettingService) SystemClean(req []dto.Clean) {
|
||||
continue
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.Name() == "1Panel.log" {
|
||||
if file.Name() == "1Panel.log" || file.IsDir() {
|
||||
continue
|
||||
}
|
||||
dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, file.Name()))
|
||||
@@ -276,7 +276,7 @@ func (u *SettingService) SystemClean(req []dto.Clean) {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *SettingService) SystemCleanForCronjob() (string, error) {
|
||||
func (u *DeviceService) CleanForCronjob() (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" {
|
||||
if treeType == "system_log" && (file.Name() == "1Panel.log" || file.IsDir()) {
|
||||
continue
|
||||
}
|
||||
if (treeType == "upload" || treeType == "download") && file.IsDir() && (file.Name() == "app" || file.Name() == "database" || file.Name() == "website" || file.Name() == "directory") {
|
||||
@@ -21,6 +21,7 @@ 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
|
||||
@@ -32,13 +33,17 @@ 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"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
type logOption struct {
|
||||
LogMaxSize string `json:"max-size"`
|
||||
@@ -110,6 +115,10 @@ 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
|
||||
@@ -149,6 +158,13 @@ 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")
|
||||
@@ -237,6 +253,48 @@ 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)
|
||||
|
||||
@@ -32,6 +32,7 @@ var (
|
||||
websiteDnsRepo = repo.NewIWebsiteDnsAccountRepo()
|
||||
websiteSSLRepo = repo.NewISSLRepo()
|
||||
websiteAcmeRepo = repo.NewIAcmeAccountRepo()
|
||||
websiteCARepo = repo.NewIWebsiteCARepo()
|
||||
|
||||
logRepo = repo.NewILogRepo()
|
||||
snapshotRepo = repo.NewISnapshotRepo()
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
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 = "-"
|
||||
return baseInfo, nil
|
||||
}
|
||||
baseInfo.Version = client.Version()
|
||||
conf, err := os.ReadFile(defaultFail2BanPath)
|
||||
if err != nil {
|
||||
return baseInfo, fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -41,6 +42,7 @@ 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 {
|
||||
@@ -134,6 +136,10 @@ 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)
|
||||
@@ -245,8 +251,11 @@ func (f *FileService) MvFile(m request.FileMove) error {
|
||||
if !fo.Stat(m.NewPath) {
|
||||
return buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
for _, path := range m.OldPaths {
|
||||
if path == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(path)+"/") {
|
||||
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)+"/") {
|
||||
return buserr.New(constant.ErrMovePathFailed)
|
||||
}
|
||||
}
|
||||
@@ -297,3 +306,62 @@ 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
|
||||
}
|
||||
|
||||
@@ -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: true, PruneChildren: true}); err != nil {
|
||||
if strings.Contains(err.Error(), "image is being used") {
|
||||
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 strings.Contains(id, "sha256:") {
|
||||
return buserr.New(constant.ErrObjectInUsed)
|
||||
}
|
||||
|
||||
@@ -217,12 +217,12 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
case "create":
|
||||
registries = common.RemoveRepeatElement(append(registries, newHost))
|
||||
case "update":
|
||||
registries = common.RemoveRepeatElement(append(registries, newHost))
|
||||
for i, regi := range registries {
|
||||
if regi == delHost {
|
||||
registries = append(registries[:i], registries[i+1:]...)
|
||||
}
|
||||
}
|
||||
registries = common.RemoveRepeatElement(append(registries, newHost))
|
||||
case "delete":
|
||||
for i, regi := range registries {
|
||||
if regi == delHost {
|
||||
|
||||
@@ -51,7 +51,7 @@ func (u *LogService) ListSystemLogFile() ([]string, error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "1Panel") {
|
||||
if info.Name() == "1Panel.log" {
|
||||
files = append(files, time.Now().Format("2006-01-02"))
|
||||
return nil
|
||||
|
||||
@@ -196,8 +196,11 @@ func opNginx(containerName, operate string) error {
|
||||
if operate == constant.NginxCheck {
|
||||
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
|
||||
}
|
||||
if out, err := cmd.ExecWithTimeOut(nginxCmd, 2*time.Second); err != nil {
|
||||
return errors.New(out)
|
||||
if out, err := cmd.ExecWithTimeOut(nginxCmd, 20*time.Second); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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.Rename(create.SourcePath, path.Join(clashDir, rName))
|
||||
return op.Mv(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.Rename(filePath, recycleBinDTO.SourcePath)
|
||||
return op.Mv(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.Rename(dir, newDir); err != nil {
|
||||
if err := op.Mv(dir, newDir); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/subosito/gotenv"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,7 +33,7 @@ type RuntimeService struct {
|
||||
|
||||
type IRuntimeService interface {
|
||||
Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error)
|
||||
Create(create request.RuntimeCreate) error
|
||||
Create(create request.RuntimeCreate) (*model.Runtime, error)
|
||||
Delete(delete request.RuntimeDelete) error
|
||||
Update(req request.RuntimeUpdate) error
|
||||
Get(id uint) (res *response.RuntimeDTO, err error)
|
||||
@@ -47,7 +48,7 @@ func NewRuntimeService() IRuntimeService {
|
||||
return &RuntimeService{}
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
)
|
||||
@@ -59,7 +60,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
}
|
||||
exist, _ := runtimeRepo.GetFirst(opts...)
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
return nil, buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
|
||||
@@ -73,44 +74,45 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
Version: create.Version,
|
||||
Status: constant.RuntimeNormal,
|
||||
}
|
||||
return runtimeRepo.Create(context.Background(), runtime)
|
||||
return nil, runtimeRepo.Create(context.Background(), runtime)
|
||||
}
|
||||
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
return nil, buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
if !fileOp.Stat(create.CodeDir) {
|
||||
return buserr.New(constant.ErrPathNotFound)
|
||||
return nil, buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
create.Install = true
|
||||
if err = checkPortExist(create.Port); err != nil {
|
||||
return err
|
||||
if err := checkPortExist(create.Port); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, export := range create.ExposedPorts {
|
||||
if err = checkPortExist(export.HostPort); err != nil {
|
||||
return err
|
||||
if err := checkPortExist(export.HostPort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if containerName, ok := create.Params["CONTAINER_NAME"]; ok {
|
||||
if err := checkContainerName(containerName.(string)); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
|
||||
|
||||
appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version)
|
||||
if !fileOp.Stat(appVersionDir) || appDetail.Update {
|
||||
if err := downloadApp(app, appDetail, nil); err != nil {
|
||||
return err
|
||||
if err = downloadApp(app, appDetail, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,15 +128,18 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
switch create.Type {
|
||||
case constant.RuntimePHP:
|
||||
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
runtime.Port = create.Port
|
||||
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return runtimeRepo.Create(context.Background(), runtime)
|
||||
if err := runtimeRepo.Create(context.Background(), runtime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runtime, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error) {
|
||||
|
||||
@@ -13,6 +13,8 @@ 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"
|
||||
@@ -21,8 +23,6 @@ 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,7 +32,6 @@ 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
|
||||
@@ -40,11 +39,6 @@ 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 {
|
||||
@@ -72,14 +66,6 @@ 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":
|
||||
@@ -107,10 +93,6 @@ 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 {
|
||||
@@ -125,17 +107,13 @@ func (u *SettingService) Update(key, value string) error {
|
||||
|
||||
switch key {
|
||||
case "ExpirationDays":
|
||||
timeout, _ := strconv.Atoi(value)
|
||||
timeout, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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()
|
||||
@@ -148,21 +126,6 @@ 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()
|
||||
@@ -190,6 +153,7 @@ 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)
|
||||
@@ -214,6 +178,7 @@ 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)
|
||||
@@ -241,7 +206,6 @@ 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
|
||||
@@ -250,49 +214,72 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
if err := settingRepo.Update("SSLType", req.SSLType); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.SSLType == "self" {
|
||||
var (
|
||||
secret string
|
||||
key string
|
||||
)
|
||||
|
||||
switch req.SSLType {
|
||||
case "self":
|
||||
if len(req.Domain) == 0 {
|
||||
return fmt.Errorf("load domain failed")
|
||||
}
|
||||
if err := ssl.GenerateSSL(req.Domain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLType == "select" {
|
||||
sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
defaultCA, err := websiteCARepo.GetFirst(commonRepo.WithByName("1Panel"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Cert = sslInfo.Pem
|
||||
req.Key = sslInfo.PrivateKey
|
||||
req.SSLType = "import"
|
||||
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
|
||||
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 err != nil {
|
||||
return err
|
||||
}
|
||||
key = string(keyFile)
|
||||
certFile, err := os.ReadFile(req.Cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret = string(certFile)
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
if _, err := key.WriteString(req.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
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 := 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
|
||||
}
|
||||
@@ -303,6 +290,7 @@ 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)
|
||||
|
||||
@@ -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 -a 'preauth' %s", file.Name, command)
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' %s", file.Name, command)
|
||||
default:
|
||||
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file.Name, command)
|
||||
}
|
||||
|
||||
@@ -173,9 +173,6 @@ 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)
|
||||
}
|
||||
@@ -619,7 +616,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
|
||||
@@ -693,12 +690,13 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
}
|
||||
|
||||
if req.Type == constant.SSLExisted {
|
||||
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
|
||||
websiteModel, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
website.WebsiteSSLID = websiteSSL.ID
|
||||
res.SSL = websiteSSL
|
||||
website.WebsiteSSLID = websiteModel.ID
|
||||
res.SSL = *websiteModel
|
||||
websiteSSL = *websiteModel
|
||||
}
|
||||
if req.Type == constant.SSLManual {
|
||||
var (
|
||||
@@ -1212,7 +1210,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldRuntime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.RuntimeID))
|
||||
oldRuntime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1270,7 +1268,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
|
||||
return busErr
|
||||
}
|
||||
|
||||
newComposeByte, busErr = changeServiceName(composePath, appInstall.ServiceName)
|
||||
newComposeByte, busErr = changeServiceName(appDetail.DockerCompose, appInstall.ServiceName)
|
||||
if busErr != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1291,6 +1289,7 @@ 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)
|
||||
@@ -2093,15 +2092,8 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
|
||||
})
|
||||
}
|
||||
case "404":
|
||||
if req.KeepPath && !req.RedirectRoot {
|
||||
target = req.Target + "$request_uri"
|
||||
}
|
||||
if req.RedirectRoot {
|
||||
target = "/"
|
||||
} else {
|
||||
if req.KeepPath {
|
||||
target = req.Target + "$request_uri"
|
||||
}
|
||||
}
|
||||
block = &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
@@ -2116,7 +2108,7 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{"301", target},
|
||||
Parameters: []string{req.Redirect, target},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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,25 +34,40 @@ 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 response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
|
||||
return nil, buserr.New(constant.ErrEmailIsExist)
|
||||
}
|
||||
acmeAccount := &model.WebsiteAcmeAccount{
|
||||
Email: create.Email,
|
||||
Type: create.Type,
|
||||
KeyType: create.KeyType,
|
||||
}
|
||||
|
||||
client, err := ssl.NewAcmeClient(create.Email, "")
|
||||
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)
|
||||
if err != nil {
|
||||
return response.WebsiteAcmeAccountDTO{}, err
|
||||
return nil, err
|
||||
}
|
||||
acmeAccount := model.WebsiteAcmeAccount{
|
||||
Email: create.Email,
|
||||
URL: client.User.Registration.URI,
|
||||
PrivateKey: string(ssl.GetPrivateKey(client.User.GetPrivateKey())),
|
||||
privateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := websiteAcmeRepo.Create(acmeAccount); err != nil {
|
||||
return response.WebsiteAcmeAccountDTO{}, err
|
||||
acmeAccount.PrivateKey = string(privateKey)
|
||||
acmeAccount.URL = client.User.Registration.URI
|
||||
|
||||
if err := websiteAcmeRepo.Create(*acmeAccount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: acmeAccount}, nil
|
||||
return &response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccountService) Delete(id uint) error {
|
||||
|
||||
@@ -0,0 +1,388 @@
|
||||
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
|
||||
}
|
||||
+272
-119
@@ -4,6 +4,7 @@ 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"
|
||||
@@ -11,11 +12,19 @@ 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 {
|
||||
@@ -26,12 +35,14 @@ 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(id uint) error
|
||||
Delete(ids []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 {
|
||||
@@ -46,9 +57,10 @@ func (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []respo
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, sslModel := range sslList {
|
||||
for _, model := range sslList {
|
||||
result = append(result, response.WebsiteSSLDTO{
|
||||
WebsiteSSL: sslModel,
|
||||
WebsiteSSL: model,
|
||||
LogPath: path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", model.PrimaryDomain, model.ID)),
|
||||
})
|
||||
}
|
||||
return total, result, err
|
||||
@@ -60,7 +72,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
|
||||
}
|
||||
|
||||
@@ -95,154 +107,203 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
||||
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
|
||||
}
|
||||
|
||||
var websiteSSL model.WebsiteSSL
|
||||
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, ",")
|
||||
|
||||
switch create.Provider {
|
||||
case constant.DNSAccount:
|
||||
if create.Provider == constant.DNSAccount || create.Provider == constant.Http {
|
||||
websiteSSL.AutoRenew = create.AutoRenew
|
||||
}
|
||||
if create.Provider == constant.DNSAccount {
|
||||
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(create.DnsAccountID))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
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
|
||||
}
|
||||
websiteSSL.DnsAccountID = dnsAccount.ID
|
||||
}
|
||||
|
||||
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) Renew(sslId uint) error {
|
||||
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(sslId))
|
||||
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))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(websiteSSL.AcmeAccountID))
|
||||
acmeAccount, err = websiteAcmeRepo.GetFirst(commonRepo.WithByID(websiteSSL.AcmeAccountID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||
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(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
|
||||
if err := client.UseHTTP(path.Join(appInstall.GetPath(), "root")); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.DnsManual:
|
||||
if err := client.UseManualDns(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
resource, err := client.RenewSSL(websiteSSL.CertURL)
|
||||
domains := []string{websiteSSL.PrimaryDomain}
|
||||
if websiteSSL.Domains != "" {
|
||||
domains = append(domains, strings.Split(websiteSSL.Domains, ",")...)
|
||||
}
|
||||
|
||||
privateKey, err := certcrypto.GeneratePrivateKey(ssl.KeyType(websiteSSL.KeyType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
|
||||
websiteSSL.Status = constant.SSLApply
|
||||
err = websiteSSLRepo.Save(websiteSSL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
|
||||
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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())
|
||||
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})
|
||||
}
|
||||
}
|
||||
if len(websites) > 0 {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
legoLogger.Logger.Println(startMsg)
|
||||
resource, err := client.ObtainSSL(domains, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
handleError(websiteSSL, err)
|
||||
return
|
||||
}
|
||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
return buserr.New(constant.ErrSSLApply)
|
||||
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
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
}()
|
||||
|
||||
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.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
||||
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -272,15 +333,34 @@ 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(id uint) error {
|
||||
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
|
||||
return buserr.New(constant.ErrSSLCannotDelete)
|
||||
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))
|
||||
}
|
||||
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
if len(names) > 0 {
|
||||
return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ","))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
|
||||
@@ -289,14 +369,23 @@ 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 {
|
||||
newSSL := &model.WebsiteSSL{
|
||||
Provider: constant.Manual,
|
||||
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
|
||||
}
|
||||
|
||||
if req.Type == "local" {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.PrivateKeyPath) {
|
||||
@@ -308,24 +397,24 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
|
||||
if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
newSSL.PrivateKey = string(content)
|
||||
websiteSSL.PrivateKey = string(content)
|
||||
}
|
||||
if content, err := fileOp.GetContent(req.CertificatePath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
newSSL.Pem = string(content)
|
||||
websiteSSL.Pem = string(content)
|
||||
}
|
||||
} else {
|
||||
newSSL.PrivateKey = req.PrivateKey
|
||||
newSSL.Pem = req.Certificate
|
||||
websiteSSL.PrivateKey = req.PrivateKey
|
||||
websiteSSL.Pem = req.Certificate
|
||||
}
|
||||
|
||||
privateKeyCertBlock, _ := pem.Decode([]byte(newSSL.PrivateKey))
|
||||
privateKeyCertBlock, _ := pem.Decode([]byte(websiteSSL.PrivateKey))
|
||||
if privateKeyCertBlock == nil {
|
||||
return buserr.New("ErrSSLKeyFormat")
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode([]byte(newSSL.Pem))
|
||||
certBlock, _ := pem.Decode([]byte(websiteSSL.Pem))
|
||||
if certBlock == nil {
|
||||
return buserr.New("ErrSSLCertificateFormat")
|
||||
}
|
||||
@@ -333,18 +422,82 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSSL.ExpireDate = cert.NotAfter
|
||||
newSSL.StartDate = cert.NotBefore
|
||||
newSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
if len(cert.Issuer.Organization) > 0 {
|
||||
newSSL.Organization = cert.Issuer.Organization[0]
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
} else {
|
||||
newSSL.Organization = cert.Issuer.CommonName
|
||||
}
|
||||
if len(cert.DNSNames) > 0 {
|
||||
newSSL.PrimaryDomain = cert.DNSNames[0]
|
||||
newSSL.Domains = strings.Join(cert.DNSNames, ",")
|
||||
websiteSSL.Organization = cert.Issuer.CommonName
|
||||
}
|
||||
|
||||
return websiteSSLRepo.Create(context.Background(), newSSL)
|
||||
var domains []string
|
||||
if len(cert.DNSNames) > 0 {
|
||||
websiteSSL.PrimaryDomain = cert.DNSNames[0]
|
||||
domains = cert.DNSNames[1:]
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ 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"
|
||||
@@ -649,11 +651,9 @@ func chownRootDir(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeServiceName(composePath, newServiceName string) (composeByte []byte, err error) {
|
||||
func changeServiceName(newComposeContent, newServiceName string) (composeByte []byte, err error) {
|
||||
composeMap := make(map[string]interface{})
|
||||
fileOp := files.NewFileOp()
|
||||
composeContent, _ := fileOp.GetContent(composePath)
|
||||
if err = yaml.Unmarshal(composeContent, &composeMap); err != nil {
|
||||
if err = yaml.Unmarshal([]byte(newComposeContent), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
@@ -748,3 +748,74 @@ 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
|
||||
}
|
||||
|
||||
@@ -36,16 +36,14 @@ 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"
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
MinIo = "MINIO"
|
||||
Cos = "COS"
|
||||
Kodo = "KODO"
|
||||
WebDAV = "WebDAV"
|
||||
|
||||
OneDriveRedirectURI = "http://localhost/login/authorized"
|
||||
)
|
||||
|
||||
@@ -6,4 +6,9 @@ const (
|
||||
DB DBContext = "db"
|
||||
|
||||
SystemRestart = "systemRestart"
|
||||
|
||||
TypeWebsite = "website"
|
||||
TypePhp = "php"
|
||||
TypeSSL = "ssl"
|
||||
TypeSystem = "system"
|
||||
)
|
||||
|
||||
@@ -16,4 +16,5 @@ var (
|
||||
RemoteAppResourceDir = path.Join(AppResourceDir, "remote")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
RecycleBinDir = "/.1panel_clash"
|
||||
SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
|
||||
)
|
||||
|
||||
@@ -60,6 +60,8 @@ var (
|
||||
ErrInstallDirNotFound = "ErrInstallDirNotFound"
|
||||
ErrContainerName = "ErrContainerName"
|
||||
ErrAppNameExist = "ErrAppNameExist"
|
||||
ErrFileNotFound = "ErrFileNotFound"
|
||||
ErrFileParseApp = "ErrFileParseApp"
|
||||
)
|
||||
|
||||
// website
|
||||
@@ -76,10 +78,11 @@ var (
|
||||
|
||||
// ssl
|
||||
var (
|
||||
ErrSSLCannotDelete = "ErrSSLCannotDelete"
|
||||
ErrAccountCannotDelete = "ErrAccountCannotDelete"
|
||||
ErrSSLApply = "ErrSSLApply"
|
||||
ErrEmailIsExist = "ErrEmailIsExist"
|
||||
ErrSSLCannotDelete = "ErrSSLCannotDelete"
|
||||
ErrAccountCannotDelete = "ErrAccountCannotDelete"
|
||||
ErrSSLApply = "ErrSSLApply"
|
||||
ErrEmailIsExist = "ErrEmailIsExist"
|
||||
ErrEabKidOrEabHmacKeyCannotBlank = "ErrEabKidOrEabHmacKeyCannotBlank"
|
||||
)
|
||||
|
||||
// file
|
||||
@@ -141,3 +144,7 @@ var (
|
||||
var (
|
||||
ErrBashExecute = "ErrBashExecute"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotExistUser = "ErrNotExistUser"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ package constant
|
||||
const (
|
||||
StatusRunning = "Running"
|
||||
StatusDone = "Done"
|
||||
StatusStoped = "Stoped"
|
||||
StatusWaiting = "Waiting"
|
||||
StatusSuccess = "Success"
|
||||
StatusFailed = "Failed"
|
||||
|
||||
@@ -27,6 +27,7 @@ const (
|
||||
DnsManual = "dnsManual"
|
||||
Http = "http"
|
||||
Manual = "manual"
|
||||
SelfSigned = "selfSigned"
|
||||
|
||||
StartWeb = "start"
|
||||
StopWeb = "stop"
|
||||
@@ -45,4 +46,10 @@ const (
|
||||
|
||||
ConfigPHP = "php"
|
||||
ConfigFPM = "fpm"
|
||||
|
||||
SSLInit = "init"
|
||||
SSLError = "error"
|
||||
SSLReady = "ready"
|
||||
SSLApply = "applying"
|
||||
SSLApplyError = "applyError"
|
||||
)
|
||||
|
||||
+41
-6
@@ -1,10 +1,15 @@
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -16,6 +21,7 @@ func NewSSLJob() *ssl {
|
||||
}
|
||||
|
||||
func (ssl *ssl) Run() {
|
||||
systemSSLEnable, sslID := service.GetSystemSSL()
|
||||
sslRepo := repo.NewISSLRepo()
|
||||
sslService := service.NewIWebsiteSSLService()
|
||||
sslList, _ := sslRepo.List()
|
||||
@@ -23,18 +29,47 @@ 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" {
|
||||
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" || s.Status == "applying" {
|
||||
continue
|
||||
}
|
||||
expireDate := s.ExpireDate.In(nyc)
|
||||
sub := expireDate.Sub(now)
|
||||
if sub.Hours() < 720 {
|
||||
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
|
||||
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("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
|
||||
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.Info("The scheduled certificate update task has completed")
|
||||
|
||||
@@ -20,6 +20,9 @@ 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"
|
||||
@@ -83,7 +86,7 @@ ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
|
||||
ErrDomainFormat: "{{ .name }} domain format error"
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||
ErrSSLCannotDelete: "The certificate {{ .name }} 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'
|
||||
@@ -91,6 +94,22 @@ 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"
|
||||
@@ -137,4 +156,8 @@ 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."
|
||||
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!"
|
||||
|
||||
@@ -20,6 +20,9 @@ TYPE_RUNTIME: "運作環境"
|
||||
TYPE_DOMAIN: "網域名稱"
|
||||
ErrTypePort: '埠 {{ .name }} 格式錯誤'
|
||||
ErrTypePortRange: '連接埠範圍需要在 1-65535 之間'
|
||||
Success: "成功"
|
||||
Failed: "失敗"
|
||||
SystemRestart: "系統重啟導致任務中斷"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
|
||||
@@ -83,7 +86,7 @@ ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
|
||||
ErrDomainFormat: "{{ .name }} 域名格式不正確"
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
|
||||
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
|
||||
ErrAccountCannotDelete: "帳號關聯證書,無法刪除"
|
||||
ErrSSLApply: "證書續簽成功,openresty reload失敗,請檢查配置!"
|
||||
ErrEmailIsExist: '郵箱已存在'
|
||||
@@ -91,6 +94,23 @@ 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: "當前用戶已存在,請重新輸入"
|
||||
@@ -137,4 +157,8 @@ ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}"
|
||||
ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
|
||||
|
||||
#cronjob
|
||||
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
|
||||
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
|
||||
ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!"
|
||||
|
||||
@@ -20,6 +20,9 @@ TYPE_RUNTIME: "运行环境"
|
||||
TYPE_DOMAIN: "域名"
|
||||
ErrTypePort: '端口 {{ .name }} 格式错误'
|
||||
ErrTypePortRange: '端口范围需要在 1-65535 之间'
|
||||
Success: "成功"
|
||||
Failed: "失败"
|
||||
SystemRestart: "系统重启导致任务中断"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
@@ -83,7 +86,7 @@ ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
|
||||
ErrDomainFormat: "{{ .name }} 域名格式不正确"
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
|
||||
ErrAccountCannotDelete: "账号关联证书,无法删除"
|
||||
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
|
||||
ErrEmailIsExist: '邮箱已存在'
|
||||
@@ -91,6 +94,22 @@ 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: "当前用户已存在,请重新输入"
|
||||
@@ -138,3 +157,7 @@ ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后
|
||||
|
||||
#cronjob
|
||||
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
|
||||
@@ -20,8 +20,10 @@ 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}
|
||||
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir, constant.SSLLogDir}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
for _, dir := range dirs {
|
||||
|
||||
@@ -10,6 +10,7 @@ func Init() {
|
||||
go syncApp()
|
||||
go syncInstalledApp()
|
||||
go syncRuntime()
|
||||
go syncSSL()
|
||||
}
|
||||
|
||||
func syncApp() {
|
||||
@@ -31,3 +32,9 @@ 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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,14 @@ 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)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
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
|
||||
},
|
||||
}
|
||||
@@ -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,12 +21,10 @@ 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)
|
||||
})
|
||||
@@ -94,6 +92,8 @@ func Routers() *gin.Engine {
|
||||
systemRouter.InitNginxRouter(PrivateGroup)
|
||||
systemRouter.InitRuntimeRouter(PrivateGroup)
|
||||
systemRouter.InitProcessRouter(PrivateGroup)
|
||||
systemRouter.InitToolboxRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteCARouter(PrivateGroup)
|
||||
}
|
||||
|
||||
return Router
|
||||
|
||||
@@ -8,6 +8,7 @@ type RouterGroup struct {
|
||||
MonitorRouter
|
||||
LogRouter
|
||||
FileRouter
|
||||
ToolboxRouter
|
||||
TerminalRouter
|
||||
CronjobRouter
|
||||
SettingRouter
|
||||
|
||||
@@ -79,6 +79,7 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ 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)
|
||||
|
||||
@@ -29,13 +29,9 @@ 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)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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.GET("/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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ 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)
|
||||
@@ -24,5 +23,7 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +86,7 @@ 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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -7,44 +7,24 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
|
||||
type cosClient struct {
|
||||
region string
|
||||
accessKey string
|
||||
secretKey string
|
||||
scType string
|
||||
Vars map[string]interface{}
|
||||
client *cosSDK.Client
|
||||
scType string
|
||||
client *cosSDK.Client
|
||||
clientWithBucket *cosSDK.Client
|
||||
}
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
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 {
|
||||
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 {
|
||||
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}
|
||||
@@ -55,11 +35,23 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
},
|
||||
})
|
||||
|
||||
return &cosClient{Vars: vars, client: client, accessKey: accessKey, secretKey: secretKey, scType: scType, region: region}, nil
|
||||
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
|
||||
}
|
||||
|
||||
func (cos cosClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, _, err := cos.client.Service.Get(context.Background())
|
||||
func (c cosClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, _, err := c.client.Service.Get(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -70,34 +62,33 @@ func (cos cosClient) ListBuckets() ([]interface{}, error) {
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
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)
|
||||
func (c cosClient) Exist(path string) (bool, error) {
|
||||
exist, err := c.clientWithBucket.Object.IsExist(context.Background(), path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
func (cos cosClient) Delete(path string) (bool, error) {
|
||||
client, err := cos.newClientWithBucket()
|
||||
func (c cosClient) Size(path string) (int64, error) {
|
||||
data, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: path})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return 0, err
|
||||
}
|
||||
if _, err := client.Object.Delete(context.Background(), path); err != nil {
|
||||
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 {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (cos cosClient) Upload(src, target string) (bool, error) {
|
||||
client, err := cos.newClientWithBucket()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
func (c cosClient) Upload(src, target string) (bool, error) {
|
||||
fileInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -107,22 +98,22 @@ func (cos cosClient) Upload(src, target string) (bool, error) {
|
||||
OptIni: &cosSDK.InitiateMultipartUploadOptions{
|
||||
ACLHeaderOptions: nil,
|
||||
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
|
||||
XCosStorageClass: cos.scType,
|
||||
XCosStorageClass: c.scType,
|
||||
},
|
||||
},
|
||||
PartSize: 200,
|
||||
}
|
||||
if _, _, err := client.Object.MultiUpload(
|
||||
if _, _, err := c.clientWithBucket.Object.MultiUpload(
|
||||
context.Background(), target, src, opt,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
if _, err := client.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
|
||||
if _, err := c.clientWithBucket.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
|
||||
ACLHeaderOptions: nil,
|
||||
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
|
||||
XCosStorageClass: cos.scType,
|
||||
XCosStorageClass: c.scType,
|
||||
},
|
||||
}); err != nil {
|
||||
return false, err
|
||||
@@ -130,31 +121,15 @@ func (cos cosClient) Upload(src, target string) (bool, error) {
|
||||
return true, 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 {
|
||||
func (c cosClient) Download(src, target string) (bool, error) {
|
||||
if _, err := c.clientWithBucket.Object.Download(context.Background(), src, target, &cosSDK.MultiDownloadOptions{}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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})
|
||||
func (c cosClient) ListObjects(prefix string) ([]string, error) {
|
||||
datas, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: prefix})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,19 +140,3 @@ func (cos 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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
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))
|
||||
}
|
||||
@@ -4,45 +4,35 @@ 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 {
|
||||
accessKey string
|
||||
secretKey string
|
||||
Vars map[string]interface{}
|
||||
client *storage.BucketManager
|
||||
bucket string
|
||||
domain string
|
||||
auth *auth.Credentials
|
||||
client *storage.BucketManager
|
||||
}
|
||||
|
||||
func NewKodoClient(vars map[string]interface{}) (*kodoClient, error) {
|
||||
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
|
||||
}
|
||||
accessKey := loadParamFromVars("accessKey", true, vars)
|
||||
secretKey := loadParamFromVars("secretKey", true, vars)
|
||||
bucket := loadParamFromVars("bucket", true, vars)
|
||||
domain := loadParamFromVars("domain", true, vars)
|
||||
|
||||
conn := qbox.NewMac(accessKey, secretKey)
|
||||
conn := auth.New(accessKey, secretKey)
|
||||
cfg := storage.Config{
|
||||
UseHTTPS: false,
|
||||
}
|
||||
bucketManager := storage.NewBucketManager(conn, &cfg)
|
||||
|
||||
return &kodoClient{Vars: vars, client: bucketManager, accessKey: accessKey, secretKey: secretKey}, nil
|
||||
return &kodoClient{client: bucketManager, auth: conn, bucket: bucket, domain: domain}, nil
|
||||
}
|
||||
|
||||
func (kodo kodoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := kodo.client.Buckets(true)
|
||||
func (k kodoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := k.client.Buckets(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -53,38 +43,33 @@ func (kodo kodoClient) ListBuckets() ([]interface{}, error) {
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (kodo kodoClient) Exist(path string) (bool, error) {
|
||||
bucket, err := kodo.GetBucket()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, err := kodo.client.Stat(bucket, path); err != 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 (kodo kodoClient) Delete(path string) (bool, error) {
|
||||
bucket, err := kodo.GetBucket()
|
||||
func (k kodoClient) Size(path string) (int64, error) {
|
||||
file, err := k.client.Stat(k.bucket, path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return 0, err
|
||||
}
|
||||
if err := kodo.client.Delete(bucket, path); err != nil {
|
||||
return file.Fsize, nil
|
||||
}
|
||||
|
||||
func (k kodoClient) Delete(path string) (bool, error) {
|
||||
if err := k.client.Delete(k.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
|
||||
}
|
||||
func (k kodoClient) Upload(src, target string) (bool, error) {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: bucket,
|
||||
Scope: k.bucket,
|
||||
}
|
||||
mac := qbox.NewMac(kodo.accessKey, kodo.secretKey)
|
||||
upToken := putPolicy.UploadToken(mac)
|
||||
upToken := putPolicy.UploadToken(k.auth)
|
||||
cfg := storage.Config{UseHTTPS: true, UseCdnDomains: false}
|
||||
resumeUploader := storage.NewResumeUploaderV2(&cfg)
|
||||
ret := storage.PutRet{}
|
||||
@@ -95,14 +80,9 @@ func (kodo kodoClient) Upload(src, target string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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)
|
||||
func (k kodoClient) Download(src, target string) (bool, error) {
|
||||
deadline := time.Now().Add(time.Second * 3600).Unix()
|
||||
privateAccessURL := storage.MakePrivateURL(mac, domain, src, deadline)
|
||||
privateAccessURL := storage.MakePrivateURL(k.auth, k.domain, src, deadline)
|
||||
|
||||
fo := files.NewFileOp()
|
||||
if err := fo.DownloadFile(privateAccessURL, target); err != nil {
|
||||
@@ -111,24 +91,11 @@ func (kodo kodoClient) Download(src, target string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (k kodoClient) ListObjects(prefix string) ([]string, error) {
|
||||
var result []string
|
||||
marker := ""
|
||||
for {
|
||||
entries, _, nextMarker, hashNext, err := kodo.client.ListFiles(bucket, prefix, "", marker, 1000)
|
||||
entries, _, nextMarker, hashNext, err := k.client.ListFiles(k.bucket, prefix, "", marker, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -14,29 +14,15 @@ import (
|
||||
)
|
||||
|
||||
type minIoClient struct {
|
||||
Vars map[string]interface{}
|
||||
bucket string
|
||||
client *minio.Client
|
||||
}
|
||||
|
||||
func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {
|
||||
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
|
||||
}
|
||||
endpoint := loadParamFromVars("endpoint", true, vars)
|
||||
accessKeyID := loadParamFromVars("accessKey", true, vars)
|
||||
secretAccessKey := loadParamFromVars("secretKey", true, vars)
|
||||
bucket := loadParamFromVars("bucket", true, vars)
|
||||
ssl := strings.Split(endpoint, ":")[0]
|
||||
if len(ssl) == 0 || (ssl != "https" && ssl != "http") {
|
||||
return nil, constant.ErrInvalidParams
|
||||
@@ -59,14 +45,11 @@ func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &minIoClient{
|
||||
client: client,
|
||||
Vars: vars,
|
||||
}, nil
|
||||
return &minIoClient{bucket: bucket, client: client}, nil
|
||||
}
|
||||
|
||||
func (minIo minIoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := minIo.client.ListBuckets(context.Background())
|
||||
func (m minIoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := m.client.ListBuckets(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,49 +60,44 @@ func (minIo minIoClient) ListBuckets() ([]interface{}, error) {
|
||||
return result, 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
|
||||
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
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
file, err := obj.Stat()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return file.Size, nil
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
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
|
||||
@@ -130,52 +108,36 @@ func (minIo minIoClient) Upload(src, target string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = minIo.client.PutObject(context.Background(), bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
_, err = m.client.PutObject(context.Background(), m.bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
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 nil, constant.ErrInvalidParams
|
||||
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
|
||||
}
|
||||
|
||||
func (m minIoClient) ListObjects(prefix string) ([]string, error) {
|
||||
opts := minio.ListObjectsOptions{
|
||||
Recursive: true,
|
||||
Prefix: prefix,
|
||||
}
|
||||
|
||||
var result []string
|
||||
for object := range minIo.client.ListObjects(context.Background(), bucket, opts) {
|
||||
for object := range m.client.ListObjects(context.Background(), m.bucket, opts) {
|
||||
if object.Err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -23,17 +23,11 @@ import (
|
||||
)
|
||||
|
||||
type oneDriveClient struct {
|
||||
Vars map[string]interface{}
|
||||
client odsdk.Client
|
||||
}
|
||||
|
||||
func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
|
||||
token := ""
|
||||
if _, ok := vars["accessToken"]; ok {
|
||||
token = vars["accessToken"].(string)
|
||||
} else {
|
||||
return nil, constant.ErrInvalidParams
|
||||
}
|
||||
token := loadParamFromVars("accessToken", true, vars)
|
||||
ctx := context.Background()
|
||||
|
||||
newToken, err := refreshToken(token)
|
||||
@@ -51,13 +45,13 @@ func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
|
||||
return &oneDriveClient{client: *client}, nil
|
||||
}
|
||||
|
||||
func (onedrive oneDriveClient) ListBuckets() ([]interface{}, error) {
|
||||
func (o oneDriveClient) ListBuckets() ([]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (onedrive oneDriveClient) Exist(path string) (bool, error) {
|
||||
func (o oneDriveClient) Exist(path string) (bool, error) {
|
||||
path = "/" + strings.TrimPrefix(path, "/")
|
||||
fileID, err := onedrive.loadIDByPath(path)
|
||||
fileID, err := o.loadIDByPath(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -65,26 +59,50 @@ func (onedrive oneDriveClient) Exist(path string) (bool, error) {
|
||||
return len(fileID) != 0, nil
|
||||
}
|
||||
|
||||
func (onedrive oneDriveClient) Delete(path string) (bool, error) {
|
||||
func (o oneDriveClient) Size(path string) (int64, error) {
|
||||
path = "/" + strings.TrimPrefix(path, "/")
|
||||
req, err := onedrive.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil)
|
||||
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)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("new request for delete file failed, err: %v \n", err)
|
||||
}
|
||||
if err := onedrive.client.Do(context.Background(), req, false, nil); err != nil {
|
||||
if err := o.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 (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
func (o oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
target = "/" + strings.TrimPrefix(target, "/")
|
||||
if _, err := onedrive.loadIDByPath(path.Dir(target)); err != nil {
|
||||
if _, err := o.loadIDByPath(path.Dir(target)); err != nil {
|
||||
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||
return false, err
|
||||
}
|
||||
if err := onedrive.createFolder(path.Dir(target)); err != nil {
|
||||
if err := o.createFolder(path.Dir(target)); err != nil {
|
||||
return false, fmt.Errorf("create dir before upload failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -105,7 +123,7 @@ func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
fileName := fileInfo.Name()
|
||||
fileSize := fileInfo.Size()
|
||||
|
||||
folderID, err := onedrive.loadIDByPath(path.Dir(target))
|
||||
folderID, err := o.loadIDByPath(path.Dir(target))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -119,13 +137,13 @@ func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
DeferCommit bool `json:"deferCommit"`
|
||||
}{sessionCreationRequestInside, false}
|
||||
|
||||
sessionCreationReq, err := onedrive.client.NewRequest("POST", apiURL, sessionCreationRequest)
|
||||
sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var sessionCreationResp *NewUploadSessionCreationResponse
|
||||
err = onedrive.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
|
||||
err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("session creation failed %w", err)
|
||||
}
|
||||
@@ -149,11 +167,11 @@ func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
bufferLast := buffer[:length]
|
||||
buffer = bufferLast
|
||||
}
|
||||
sessionFileUploadReq, err := onedrive.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
|
||||
sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := onedrive.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
|
||||
if err := o.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
@@ -164,14 +182,14 @@ func (onedrive oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (onedrive oneDriveClient) Download(src, target string) (bool, error) {
|
||||
func (o oneDriveClient) Download(src, target string) (bool, error) {
|
||||
src = "/" + strings.TrimPrefix(src, "/")
|
||||
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/root:%s", src), nil)
|
||||
req, err := o.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 := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||
if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||
return false, fmt.Errorf("do request for file id failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -196,19 +214,19 @@ func (onedrive oneDriveClient) Download(src, target string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (onedrive *oneDriveClient) ListObjects(prefix string) ([]string, error) {
|
||||
func (o *oneDriveClient) ListObjects(prefix string) ([]string, error) {
|
||||
prefix = "/" + strings.TrimPrefix(prefix, "/")
|
||||
folderID, err := onedrive.loadIDByPath(prefix)
|
||||
folderID, err := o.loadIDByPath(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/items/%s/children", folderID), nil)
|
||||
req, err := o.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 := onedrive.client.Do(context.Background(), req, false, &driveItems); err != nil {
|
||||
if err := o.client.Do(context.Background(), req, false, &driveItems); err != nil {
|
||||
return nil, fmt.Errorf("do request for list failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -219,17 +237,17 @@ func (onedrive *oneDriveClient) ListObjects(prefix string) ([]string, error) {
|
||||
return itemList, nil
|
||||
}
|
||||
|
||||
func (onedrive *oneDriveClient) loadIDByPath(path string) (string, error) {
|
||||
func (o *oneDriveClient) loadIDByPath(path string) (string, error) {
|
||||
pathItem := "root:" + path
|
||||
if path == "/" {
|
||||
pathItem = "root"
|
||||
}
|
||||
req, err := onedrive.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
|
||||
req, err := o.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 := onedrive.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||
if err := o.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
|
||||
@@ -269,18 +287,18 @@ func refreshToken(oldToken string) (string, error) {
|
||||
return accessToken, nil
|
||||
}
|
||||
|
||||
func (onedrive *oneDriveClient) createFolder(parent string) error {
|
||||
if _, err := onedrive.loadIDByPath(path.Dir(parent)); err != nil {
|
||||
func (o *oneDriveClient) createFolder(parent string) error {
|
||||
if _, err := o.loadIDByPath(path.Dir(parent)); err != nil {
|
||||
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||
return err
|
||||
}
|
||||
_ = onedrive.createFolder(path.Dir(parent))
|
||||
_ = o.createFolder(path.Dir(parent))
|
||||
}
|
||||
item2, err := onedrive.loadIDByPath(path.Dir(parent))
|
||||
item2, err := o.loadIDByPath(path.Dir(parent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := onedrive.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
|
||||
if _, err := o.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -307,8 +325,8 @@ type DriveItem struct {
|
||||
WebURL string `json:"webUrl"`
|
||||
}
|
||||
|
||||
func (onedrive *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
|
||||
apiUrl, err := onedrive.client.BaseURL.Parse(absoluteUrl)
|
||||
func (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
|
||||
apiUrl, err := o.client.BaseURL.Parse(absoluteUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,54 +1,36 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"fmt"
|
||||
|
||||
osssdk "github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
)
|
||||
|
||||
type ossClient struct {
|
||||
scType string
|
||||
Vars map[string]interface{}
|
||||
client osssdk.Client
|
||||
scType string
|
||||
bucketStr string
|
||||
client osssdk.Client
|
||||
}
|
||||
|
||||
func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
|
||||
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 {
|
||||
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 {
|
||||
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,
|
||||
Vars: vars,
|
||||
client: *client,
|
||||
}, nil
|
||||
|
||||
return &ossClient{scType: scType, bucketStr: bucketStr, client: *client}, nil
|
||||
}
|
||||
|
||||
func (oss ossClient) ListBuckets() ([]interface{}, error) {
|
||||
response, err := oss.client.ListBuckets()
|
||||
func (o ossClient) ListBuckets() ([]interface{}, error) {
|
||||
response, err := o.client.ListBuckets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,69 +41,72 @@ func (oss ossClient) ListBuckets() ([]interface{}, error) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (oss ossClient) Exist(path string) (bool, error) {
|
||||
bucket, err := oss.GetBucket()
|
||||
func (o ossClient) Exist(path string) (bool, error) {
|
||||
bucket, err := o.client.Bucket(o.bucketStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return bucket.IsObjectExist(path)
|
||||
|
||||
}
|
||||
|
||||
func (oss ossClient) Delete(path string) (bool, error) {
|
||||
bucket, err := oss.GetBucket()
|
||||
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)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = bucket.DeleteObject(path)
|
||||
if err != nil {
|
||||
if err := bucket.DeleteObject(path); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (oss ossClient) Upload(src, target string) (bool, error) {
|
||||
bucket, err := oss.GetBucket()
|
||||
func (o ossClient) Upload(src, target string) (bool, error) {
|
||||
bucket, err := o.client.Bucket(o.bucketStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = bucket.UploadFile(target, src, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""), osssdk.ObjectStorageClass(osssdk.StorageClassType(oss.scType)))
|
||||
if err != nil {
|
||||
if err := bucket.UploadFile(target, src,
|
||||
200*1024*1024,
|
||||
osssdk.Routines(5),
|
||||
osssdk.Checkpoint(true, ""),
|
||||
osssdk.ObjectStorageClass(osssdk.StorageClassType(o.scType))); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (oss ossClient) Download(src, target string) (bool, error) {
|
||||
bucket, err := oss.GetBucket()
|
||||
func (o ossClient) Download(src, target string) (bool, error) {
|
||||
bucket, err := o.client.Bucket(o.bucketStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""))
|
||||
if err != nil {
|
||||
if err := bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, "")); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func (oss *ossClient) ListObjects(prefix string) ([]string, error) {
|
||||
bucket, err := oss.GetBucket()
|
||||
func (o *ossClient) ListObjects(prefix string) ([]string, error) {
|
||||
bucket, err := o.client.Bucket(o.bucketStr)
|
||||
if err != nil {
|
||||
return nil, constant.ErrInvalidParams
|
||||
return nil, err
|
||||
}
|
||||
lor, err := bucket.ListObjects(osssdk.Prefix(prefix))
|
||||
lor, err := bucket.ListObjectsV2(osssdk.Prefix(prefix))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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"
|
||||
@@ -14,41 +13,20 @@ import (
|
||||
|
||||
type s3Client struct {
|
||||
scType string
|
||||
Vars map[string]interface{}
|
||||
bucket string
|
||||
Sess session.Session
|
||||
}
|
||||
|
||||
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
||||
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 {
|
||||
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 {
|
||||
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),
|
||||
@@ -59,16 +37,12 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3Client{
|
||||
scType: scType,
|
||||
Vars: vars,
|
||||
Sess: *sess,
|
||||
}, nil
|
||||
return &s3Client{scType: scType, bucket: bucket, Sess: *sess}, nil
|
||||
}
|
||||
|
||||
func (s3C s3Client) ListBuckets() ([]interface{}, error) {
|
||||
func (s s3Client) ListBuckets() ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
svc := s3.New(&s3C.Sess)
|
||||
svc := s3.New(&s.Sess)
|
||||
res, err := svc.ListBuckets(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -79,17 +53,12 @@ func (s3C s3Client) ListBuckets() ([]interface{}, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
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,
|
||||
func (s s3Client) Exist(path string) (bool, error) {
|
||||
svc := s3.New(&s.Sess)
|
||||
if _, err := svc.HeadObject(&s3.HeadObjectInput{
|
||||
Bucket: &s.bucket,
|
||||
Key: &path,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
if aerr, ok := err.(awserr.RequestFailure); ok {
|
||||
if aerr.StatusCode() == 404 {
|
||||
return false, nil
|
||||
@@ -101,57 +70,53 @@ func (s3C s3Client) Exist(path string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s3C s3Client) Delete(path string) (bool, error) {
|
||||
bucket, err := s3C.getBucket()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
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),
|
||||
func (s *s3Client) Size(path string) (int64, error) {
|
||||
svc := s3.New(&s.Sess)
|
||||
file, err := svc.GetObject(&s3.GetObjectInput{
|
||||
Bucket: &s.bucket,
|
||||
Key: &path,
|
||||
})
|
||||
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),
|
||||
Key: aws.String(path),
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s3C s3Client) Upload(src, target string) (bool, error) {
|
||||
bucket, err := s3C.getBucket()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
func (s s3Client) Upload(src, target string) (bool, error) {
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
uploader := s3manager.NewUploader(&s3C.Sess)
|
||||
_, err = uploader.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String(bucket),
|
||||
uploader := s3manager.NewUploader(&s.Sess)
|
||||
if _, err := uploader.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(target),
|
||||
Body: file,
|
||||
StorageClass: &s3C.scType,
|
||||
})
|
||||
if err != nil {
|
||||
StorageClass: &s.scType,
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, 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 {
|
||||
func (s s3Client) Download(src, target string) (bool, error) {
|
||||
if _, err := os.Stat(target); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
os.Remove(target)
|
||||
} else {
|
||||
@@ -163,44 +128,29 @@ func (s3C s3Client) Download(src, target string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
downloader := s3manager.NewDownloader(&s3C.Sess)
|
||||
_, err = downloader.Download(file,
|
||||
&s3.GetObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(src),
|
||||
})
|
||||
if err != nil {
|
||||
downloader := s3manager.NewDownloader(&s.Sess)
|
||||
if _, err = downloader.Download(file, &s3.GetObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(src),
|
||||
}); err != nil {
|
||||
os.Remove(target)
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s3C *s3Client) getBucket() (string, error) {
|
||||
if _, ok := s3C.Vars["bucket"]; ok {
|
||||
return s3C.Vars["bucket"].(string), nil
|
||||
} else {
|
||||
return "", constant.ErrInvalidParams
|
||||
}
|
||||
}
|
||||
|
||||
func (s3C *s3Client) ListObjects(prefix string) ([]string, error) {
|
||||
bucket, err := s3C.getBucket()
|
||||
if err != nil {
|
||||
return nil, constant.ErrInvalidParams
|
||||
}
|
||||
svc := s3.New(&s3C.Sess)
|
||||
func (s *s3Client) ListObjects(prefix string) ([]string, error) {
|
||||
svc := s3.New(&s.Sess)
|
||||
var result []string
|
||||
if err := svc.ListObjectsPages(&s3.ListObjectsInput{
|
||||
Bucket: &bucket,
|
||||
outputs, err := svc.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: &s.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
|
||||
})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
for _, item := range outputs.Contents {
|
||||
result = append(result, *item.Key)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user