1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 00:09:16 +08:00

feat: 增加证书申请

This commit is contained in:
zhengkunwang223 2022-11-16 10:31:35 +08:00 committed by zhengkunwang223
parent ac864ff4e7
commit 681a0c9106
31 changed files with 795 additions and 173 deletions

View File

@ -12,6 +12,7 @@ services:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./log:/var/log/nginx
- ./conf/conf.d:/etc/nginx/conf.d/
- ./ssl:/etc/nginx/ssl
networks:
1panel:

View File

@ -24,43 +24,59 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
})
}
//func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
// var req dto.WebsiteSSLCreate
// if err := c.ShouldBindJSON(&req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
// if _, err := WebsiteSSLService.Create(req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}
//
//func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
// var req dto.WebsiteSSLUpdate
// if err := c.ShouldBindJSON(&req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
// if _, err := WebsiteSSLService.Update(req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}
//
//func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
//
// id, err := helper.GetParamID(c)
// if err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
//
// if err := WebsiteSSLService.Delete(id); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}
func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
var req dto.WebsiteSSLCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteSSLService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
var req dto.WebsiteSSLApply
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteSSLService.Apply(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
var req dto.WebsiteDNSReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteSSLService.GetDNSResolve(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteSSLService.Delete(id); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View File

@ -21,6 +21,7 @@ type NginxScope string
const (
Index NginxScope = "index"
LimitConn NginxScope = "limit-conn"
SSL NginxScope = "ssl"
)
type NginxOp string
@ -34,6 +35,7 @@ const (
var ScopeKeyMap = map[NginxScope][]string{
Index: {"index"},
LimitConn: {"limit_conn", "limit_rate", "limit_conn_zone"},
SSL: {"ssl_certificate", "ssl_certificate_key"},
}
var RepeatKeys = map[string]struct {

View File

@ -28,6 +28,10 @@ type WebSiteCreate struct {
WebSiteGroupID uint `json:"webSiteGroupID" validate:"required"`
}
type WebsiteDTO struct {
model.WebSite
}
type WebSiteUpdate struct {
ID uint `json:"id" validate:"required"`
PrimaryDomain string `json:"primaryDomain" validate:"required"`

View File

@ -6,8 +6,34 @@ type WebsiteSSLDTO struct {
model.WebSiteSSL
}
type SSLProvider string
const (
DNSAccount = "dnsAccount"
DnsCommon = "dnsCommon"
Http = "http"
)
type WebsiteSSLCreate struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
Authorization map[string]string `json:"authorization" validate:"required"`
Domains []string `json:"domains" validate:"required"`
Provider SSLProvider `json:"provider" validate:"required"`
WebsiteID uint `json:"websiteId" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId"`
DnsAccountID uint `json:"dnsAccountId"`
}
type WebsiteSSLApply struct {
WebsiteID uint `json:"websiteId" validate:"required"`
SSLID uint `json:"SSLId" validate:"required"`
}
type WebsiteDNSReq struct {
Domains []string `json:"domains" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
}
type WebsiteDNSRes struct {
Key string `json:"resolve"`
Value string `json:"value"`
Type string `json:"type"`
}

View File

@ -4,17 +4,18 @@ import "time"
type WebSite struct {
BaseModel
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
Remark string `gorm:"type:longtext;" json:"remark"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
ExpireDate time.Time `json:"expireDate"`
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
WebsiteDnsAccountID uint `gorm:"type:integer" json:"websiteDnsAccountId"`
WebsiteAcmeAccountID uint `gorm:"type:integer" json:"websiteAcmeAccountId"`
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
Remark string `gorm:"type:longtext;" json:"remark"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
ExpireDate time.Time `json:"expireDate"`
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
WebsiteDnsAccountID uint `gorm:"type:integer" json:"websiteDnsAccountId"`
WebsiteAcmeAccountID uint `gorm:"type:integer" json:"websiteAcmeAccountId"`
Domains []WebSiteDomain `json:"domains"`
}
func (w WebSite) TableName() string {

View File

@ -4,14 +4,16 @@ import "time"
type WebSiteSSL struct {
BaseModel
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
Pem string `gorm:"type:longtext;not null" json:"pem"`
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
IssuerName string `gorm:"type:varchar(64);not null" json:"issuerName"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Alias string `gorm:"type:varchar(64);not null" json:"alias"`
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
Pem string `gorm:"type:longtext;not null" json:"pem"`
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
IssuerName string `gorm:"type:varchar(64);not null" json:"issuerName"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
}
func (w WebSiteSSL) TableName() string {

View File

@ -28,7 +28,7 @@ func (w WebSiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebS
func (w WebSiteRepo) GetFirst(opts ...DBOption) (model.WebSite, error) {
var website model.WebSite
db := getDb(opts...).Model(&model.WebSite{})
if err := db.First(&website).Error; err != nil {
if err := db.Preload("Domains").First(&website).Error; err != nil {
return website, err
}
return website, nil

View File

@ -14,6 +14,15 @@ func (w WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, [
return count, accounts, err
}
func (w WebsiteAcmeAccountRepo) GetFirst(opts ...DBOption) (model.WebsiteAcmeAccount, error) {
var account model.WebsiteAcmeAccount
db := getDb(opts...).Model(&model.WebsiteAcmeAccount{})
if err := db.First(&account).Error; err != nil {
return account, err
}
return account, nil
}
func (w WebsiteAcmeAccountRepo) Create(account model.WebsiteAcmeAccount) error {
return getDb().Create(&account).Error
}

View File

@ -16,6 +16,15 @@ func (w WebsiteDnsAccountRepo) Page(page, size int, opts ...DBOption) (int64, []
return count, accounts, err
}
func (w WebsiteDnsAccountRepo) GetFirst(opts ...DBOption) (model.WebsiteDnsAccount, error) {
var account model.WebsiteDnsAccount
db := getDb(opts...).Model(&model.WebsiteDnsAccount{})
if err := db.First(&account).Error; err != nil {
return account, err
}
return account, nil
}
func (w WebsiteDnsAccountRepo) Create(account model.WebsiteDnsAccount) error {
return getDb().Create(&account).Error
}

View File

@ -1,10 +1,20 @@
package repo
import "github.com/1Panel-dev/1Panel/backend/app/model"
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
"gorm.io/gorm"
)
type WebsiteSSLRepo struct {
}
func (w WebsiteSSLRepo) ByAlias(alias string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("alias = ?", alias)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteSSL, error) {
var sslList []model.WebSiteSSL
db := getDb(opts...).Model(&model.WebSiteSSL{})
@ -14,8 +24,17 @@ func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.W
return count, sslList, err
}
func (w WebsiteSSLRepo) Create(ssl model.WebSiteSSL) error {
return getDb().Create(&ssl).Error
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebSiteSSL, error) {
var website model.WebSiteSSL
db := getDb(opts...).Model(&model.WebSiteSSL{})
if err := db.First(&website).Error; err != nil {
return website, err
}
return website, nil
}
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 {

View File

@ -99,12 +99,14 @@ func (w WebsiteService) UpdateWebsite(req dto.WebSiteUpdate) error {
return websiteRepo.Save(context.TODO(), &website)
}
func (w WebsiteService) GetWebsite(id uint) (model.WebSite, error) {
func (w WebsiteService) GetWebsite(id uint) (dto.WebsiteDTO, error) {
var res dto.WebsiteDTO
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return website, err
return res, err
}
return website, nil
res.WebSite = website
return res, nil
}
func (w WebsiteService) DeleteWebSite(req dto.WebSiteDel) error {

View File

@ -1,7 +1,13 @@
package service
import (
"context"
"crypto/x509"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
"path"
"strings"
)
type WebSiteSSLService struct {
@ -18,44 +24,115 @@ func (w WebSiteSSLService) Page(search dto.PageInfo) (int64, []dto.WebsiteSSLDTO
return total, sslDTOs, err
}
//func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
//
// authorization, err := json.Marshal(create.Authorization)
// if err != nil {
// return dto.WebsiteSSLCreate{}, err
// }
//
// if err := websiteSSLRepo.Create(model.WebsiteDnsAccount{
// Name: create.Name,
// Type: create.Type,
// Authorization: string(authorization),
// }); err != nil {
// return dto.WebsiteSSLCreate{}, err
// }
//
// return create, nil
//}
//
//func (w WebSiteSSLService) Update(update dto.WebsiteDnsAccountUpdate) (dto.WebsiteDnsAccountUpdate, error) {
//
// authorization, err := json.Marshal(update.Authorization)
// if err != nil {
// return dto.WebsiteDnsAccountUpdate{}, err
// }
//
// if err := websiteSSLRepo.Save(model.WebsiteDnsAccount{
// BaseModel: model.BaseModel{
// ID: update.ID,
// },
// Name: update.Name,
// Type: update.Type,
// Authorization: string(authorization),
// }); err != nil {
// return dto.WebsiteDnsAccountUpdate{}, err
// }
//
// return update, nil
//}
func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
var res dto.WebsiteSSLCreate
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(create.AcmeAccountID))
if err != nil {
return res, err
}
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(create.DnsAccountID))
if err != nil {
return res, err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
if err != nil {
return res, err
}
if create.Provider == dto.Http {
} else {
if err := client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
return res, err
}
}
resource, err := client.GetSSL(create.Domains)
if err != nil {
return res, err
}
var websiteSSL model.WebSiteSSL
//TODO 判断同一个账号下的证书
websiteSSL.Alias = create.Domains[0]
websiteSSL.Domain = strings.Join(create.Domains, ",")
websiteSSL.PrivateKey = string(resource.PrivateKey)
websiteSSL.Pem = string(resource.Certificate)
websiteSSL.CertURL = resource.CertURL
cert, err := x509.ParseCertificate([]byte(websiteSSL.Pem))
if err != nil {
return dto.WebsiteSSLCreate{}, err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.IssuerName = cert.Issuer.Organization[0]
if err := createPemFile(websiteSSL); err != nil {
return dto.WebsiteSSLCreate{}, err
}
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
return res, err
}
return create, nil
}
func (w WebSiteSSLService) Apply(apply dto.WebsiteSSLApply) (dto.WebsiteSSLApply, error) {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(apply.SSLID))
if err != nil {
return dto.WebsiteSSLApply{}, err
}
website, err := websiteRepo.GetFirst(commonRepo.WithByID(apply.WebsiteID))
if err != nil {
return dto.WebsiteSSLApply{}, err
}
if err := createPemFile(websiteSSL); err != nil {
return dto.WebsiteSSLApply{}, err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL)
for i, param := range nginxParams {
if param.Name == "ssl_certificate" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.Alias, "fullchain.pem")}
}
if param.Name == "ssl_certificate_key" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.Alias, "privkey.pem")}
}
}
if err := updateNginxConfig(website, nginxParams, dto.SSL); err != nil {
return dto.WebsiteSSLApply{}, err
}
website.WebSiteSSLID = websiteSSL.ID
if err := websiteRepo.Save(context.TODO(), &website); err != nil {
return dto.WebsiteSSLApply{}, err
}
return apply, nil
}
func (w WebSiteSSLService) GetDNSResolve(req dto.WebsiteDNSReq) (dto.WebsiteDNSRes, error) {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID))
if err != nil {
return dto.WebsiteDNSRes{}, err
}
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
if err != nil {
return dto.WebsiteDNSRes{}, err
}
re, err := client.UseManualDns(req.Domains)
if err != nil {
return dto.WebsiteDNSRes{}, err
}
var res dto.WebsiteDNSRes
res.Key = re.Key
res.Value = re.Value
res.Type = "TXT"
return res, nil
}
func (w WebSiteSSLService) Delete(id uint) error {
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))

View File

@ -15,6 +15,7 @@ import (
"gorm.io/gorm"
"os"
"path"
"reflect"
"strconv"
"strings"
)
@ -258,16 +259,38 @@ func updateNginxConfig(website model.WebSite, params []dto.NginxParam, scope dto
}
func updateConfig(config *components.Config, scope dto.NginxScope) {
if scope == dto.LimitConn {
limit := parser.NewStringParser(string(nginx_conf.Limit)).Parse()
for _, dir := range limit.GetDirectives() {
newDir := components.Directive{
Name: dir.GetName(),
Parameters: dir.GetParameters(),
}
config.UpdateDirectiveBySecondKey(dir.GetName(), dir.GetParameters()[0], newDir)
}
newConfig := &components.Config{}
switch scope {
case dto.LimitConn:
newConfig = parser.NewStringParser(string(nginx_conf.Limit)).Parse()
}
if reflect.DeepEqual(newConfig, &components.Config{}) {
return
}
for _, dir := range newConfig.GetDirectives() {
newDir := components.Directive{
Name: dir.GetName(),
Parameters: dir.GetParameters(),
}
config.UpdateDirectiveBySecondKey(dir.GetName(), dir.GetParameters()[0], newDir)
}
}
func getNginxParamsFromStaticFile(scope dto.NginxScope) []dto.NginxParam {
var nginxParams []dto.NginxParam
newConfig := &components.Config{}
switch scope {
case dto.SSL:
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
}
for _, dir := range newConfig.GetDirectives() {
nginxParams = append(nginxParams, dto.NginxParam{
Name: dir.GetName(),
Params: dir.GetParameters(),
})
}
return nginxParams
}
func deleteNginxConfig(website model.WebSite, keys []string) error {
@ -285,6 +308,48 @@ func deleteNginxConfig(website model.WebSite, keys []string) error {
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
}
func createPemFile(websiteSSL model.WebSiteSSL) error {
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
if err != nil {
return err
}
nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))
if err != nil {
return err
}
configDir := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "ssl", websiteSSL.Alias)
fileOp := files.NewFileOp()
if !fileOp.Stat(configDir) {
if err := fileOp.CreateDir(configDir, 0775); err != nil {
return err
}
}
fullChainFile := path.Join(configDir, "fullchain.pem")
privatePemFile := path.Join(configDir, "privkey.pem")
if !fileOp.Stat(fullChainFile) {
if err := fileOp.CreateFile(fullChainFile); err != nil {
return err
}
}
if !fileOp.Stat(privatePemFile) {
if err := fileOp.CreateFile(privatePemFile); err != nil {
return err
}
}
if err := fileOp.WriteFile(fullChainFile, strings.NewReader(websiteSSL.Pem), 0644); err != nil {
return err
}
if err := fileOp.WriteFile(privatePemFile, strings.NewReader(websiteSSL.PrivateKey), 0644); err != nil {
return err
}
return nil
}
func getParamArray(key string, param interface{}) []string {
var res []string
switch param.(type) {

View File

@ -83,6 +83,7 @@ func Routers() *gin.Engine {
systemRouter.InitWebsiteGroupRouter(PrivateGroup)
systemRouter.InitWebsiteDnsAccountRouter(PrivateGroup)
systemRouter.InitDatabaseRouter(PrivateGroup)
systemRouter.InitWebsiteSSLRouter(PrivateGroup)
systemRouter.InitWebsiteAcmeAccountRouter(PrivateGroup)
}

View File

@ -18,6 +18,7 @@ type RouterGroup struct {
WebsiteGroupRouter
WebsiteDnsAccountRouter
WebsiteAcmeAccountRouter
WebsiteSSLRouter
DatabaseRouter
}

View File

@ -15,8 +15,8 @@ func (a *WebsiteDnsAccountRouter) InitWebsiteDnsAccountRouter(Router *gin.Router
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("", baseApi.PageWebsiteDnsAccount)
groupRouter.POST("/create", baseApi.CreateWebsiteDnsAccount)
groupRouter.POST("/search", baseApi.PageWebsiteDnsAccount)
groupRouter.POST("", baseApi.CreateWebsiteDnsAccount)
groupRouter.POST("/update", baseApi.UpdateWebsiteDnsAccount)
groupRouter.DELETE("/:id", baseApi.DeleteWebsiteDnsAccount)
}

View File

@ -15,7 +15,10 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("", baseApi.PageWebsiteSSL)
groupRouter.POST("/search", baseApi.PageWebsiteSSL)
groupRouter.POST("", baseApi.CreateWebsiteSSL)
groupRouter.POST("/apply", baseApi.ApplyWebsiteSSL)
groupRouter.POST("/resolve", baseApi.GetDNSResolve)
groupRouter.POST("/:id", baseApi.DeleteWebsiteSSL)
}
}

View File

@ -1,27 +1,27 @@
-----BEGIN privateKey-----
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
-----END privateKey-----

View File

@ -7,6 +7,7 @@ import (
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/registration"
"github.com/pkg/errors"
@ -58,7 +59,8 @@ func NewAcmeClient(email, privateKey string) (*AcmeClient, error) {
type DnsType string
const (
DnsPod DnsType = "dnsPod"
DnsPod DnsType = "DnsPod"
AliYun DnsType = "AliYun"
)
type DNSParam struct {
@ -87,9 +89,37 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
return err
}
}
if dnsType == AliYun {
alidnsConfig := alidns.NewDefaultConfig()
alidnsConfig.SecretKey = param.SecretKey
alidnsConfig.APIKey = param.AccessKey
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
if err != nil {
return err
}
}
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(6*time.Minute))
}
func (c *AcmeClient) UseManualDns(domains []string) (*Resolve, error) {
p := &manualDnsProvider{}
if err := c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(6*time.Minute)); err != nil {
return nil, nil
}
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
_, err := c.Client.Certificate.Obtain(request)
if err != nil {
return nil, err
}
return p.Resolve, nil
}
func (c *AcmeClient) UseHTTP() {
}
@ -108,3 +138,29 @@ func (c *AcmeClient) GetSSL(domains []string) (certificate.Resource, error) {
return *certificates, nil
}
type Resolve struct {
Key string
Value string
}
type manualDnsProvider struct {
Resolve *Resolve
}
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
fqdn, value := dns01.GetRecord(domain, keyAuth)
p.Resolve = &Resolve{
Key: fqdn,
Value: value,
}
return nil
}
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
return nil
}
func (c *AcmeClient) GetDNSResolve() {
}

View File

@ -14,6 +14,19 @@ export namespace WebSite {
appinstall?: NewAppInstall;
}
export interface WebSiteDTO extends CommonModel {
primaryDomain: string;
type: string;
alias: string;
remark: string;
domains: WebSite.Domain[];
appType: string;
appInstallId?: number;
webSiteGroupId: number;
otherDomains: string;
appinstall?: NewAppInstall;
}
export interface NewAppInstall {
name: string;
appDetailId: number;
@ -115,6 +128,19 @@ export namespace WebSite {
startDate: string;
}
export interface SSLCreate {
domains: string[];
provider: string;
websiteId: number;
acmeAccountId: number;
dnsAccountId: number;
}
export interface SSLApply {
websiteId: number;
SSLId: number;
}
export interface AcmeAccount extends CommonModel {
email: string;
url: string;
@ -123,4 +149,15 @@ export namespace WebSite {
export interface AcmeAccountCreate {
email: string;
}
export interface DNSResolveReq {
domains: string[];
acmeAccountId: number;
}
export interface DNSResolve {
key: string;
value: string;
type: string;
}
}

View File

@ -15,7 +15,7 @@ export const UpdateWebsite = (req: WebSite.WebSiteUpdateReq) => {
};
export const GetWebsite = (id: number) => {
return http.get<WebSite.WebSite>(`/websites/${id}`);
return http.get<WebSite.WebSiteDTO>(`/websites/${id}`);
};
export const DeleteWebsite = (req: WebSite.WebSiteDel) => {
@ -59,11 +59,11 @@ export const UpdateNginxConfig = (req: WebSite.NginxConfigReq) => {
};
export const SearchDnsAccount = (req: ReqPage) => {
return http.post<ResPage<WebSite.DnsAccount>>(`/websites/dns`, req);
return http.post<ResPage<WebSite.DnsAccount>>(`/websites/dns/search`, req);
};
export const CreateDnsAccount = (req: WebSite.DnsAccountCreate) => {
return http.post<any>(`/websites/dns/create`, req);
return http.post<any>(`/websites/dns`, req);
};
export const UpdateDnsAccount = (req: WebSite.DnsAccountUpdate) => {
@ -74,10 +74,6 @@ export const DeleteDnsAccount = (id: number) => {
return http.delete<any>(`/websites/dns/${id}`);
};
export const SearchSSL = (req: ReqPage) => {
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl`, req);
};
export const SearchAcmeAccount = (req: ReqPage) => {
return http.post<ResPage<WebSite.AcmeAccount>>(`/websites/acme/search`, req);
};
@ -89,3 +85,23 @@ export const CreateAcmeAccount = (req: WebSite.AcmeAccountCreate) => {
export const DeleteAcmeAccount = (id: number) => {
return http.delete<any>(`/websites/acme/${id}`);
};
export const SearchSSL = (req: ReqPage) => {
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl/search`, req);
};
export const CreateSSL = (req: WebSite.SSLCreate) => {
return http.post<WebSite.SSLCreate>(`/websites/ssl`, req);
};
export const DeleteSSL = (id: number) => {
return http.delete<any>(`/websites/ssl/${id}`);
};
export const ApplySSL = (req: WebSite.SSLApply) => {
return http.post<WebSite.SSLApply>(`/websites/ssl/apply`, req);
};
export const GetDnsResolve = (req: WebSite.DNSResolveReq) => {
return http.post<WebSite.DNSResolve>(`/websites/ssl/resolve`, req);
};

View File

@ -31,7 +31,7 @@ export const useDeleteData = <P = any, R = any>(
if (!res) return reject(false);
ElMessage({
type: 'success',
message: i18n.global.t('commons.msg.deleteSuccess'),
message: i18n.global.t('commons.msg.operationSuccess'),
});
resolve(true);
})

View File

@ -692,7 +692,7 @@ export default {
perip: '单IP限制',
peripHelper: '限制单个IP访问最大并发数',
rate: '流量限制',
rateHelper: '限制每个请求的流量上单位KB',
rateHelper: '限制每个请求的流量上(单位:KB)',
limtHelper: '启用流量控制',
other: '其他',
currentSSL: '当前证书',
@ -707,5 +707,12 @@ export default {
accountManage: '账户管理',
email: '邮箱',
addAccount: '新增账户',
acmeAccount: 'Acme 账户',
provider: '验证方式',
dnsCommon: '手动解析',
expireDate: '到期时间',
brand: '品牌',
deploySSL: '部署',
deploySSLHelper: '确定部署证书',
},
};

View File

@ -16,15 +16,15 @@
<script setup lang="ts">
import LayoutContent from '@/layout/layout-content.vue';
import { computed, onMounted, ref } from 'vue';
import { onMounted, ref } from 'vue';
import Basic from './basic/index.vue';
import SSL from './ssl/index.vue';
import router from '@/routers';
const props = defineProps({
id: {
type: Number,
default: 0,
type: String,
default: '0',
},
tab: {
type: String,
@ -32,10 +32,7 @@ const props = defineProps({
},
});
const id = computed(() => {
return props.id;
});
let id = ref(0);
let index = ref('basic');
const changeTab = (index: string) => {
@ -43,6 +40,7 @@ const changeTab = (index: string) => {
};
onMounted(() => {
id.value = Number(props.id);
if (props.tab !== index.value) {
index.value = props.tab;
}

View File

@ -65,7 +65,7 @@ const types = [
},
{
label: i18n.global.t('website.aliyun'),
value: 'Aliyun',
value: 'AliYun',
},
{
label: 'CloudFlare',

View File

@ -7,7 +7,7 @@
<Account :id="id" v-if="index == '1'"></Account>
</el-tab-pane>
<el-tab-pane :label="$t('website.applySSL')">
<SSL v-if="index == '2'"></SSL>
<SSL :id="id" v-if="index == '2'"></SSL>
</el-tab-pane>
</el-tabs>
</template>

View File

@ -0,0 +1,190 @@
<template>
<el-dialog v-model="open" :title="$t('commons.button.create')" width="30%" :before-close="handleClose">
<el-form
ref="sslForm"
label-position="right"
:model="ssl"
label-width="150px"
:rules="rules"
v-loading="loading"
>
<el-form-item :label="$t('website.acmeAccount')" prop="acmeAccountId">
<el-select v-model="ssl.acmeAccountId">
<el-option
v-for="(acme, index) in acmeAccounts"
:key="index"
:label="acme.email"
:value="acme.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('website.provider')" prop="provider">
<el-radio-group v-model="ssl.provider">
<el-radio label="dnsAccount">{{ $t('website.dnsAccount') }}</el-radio>
<el-radio label="dnsCommon">{{ $t('website.dnsCommon') }}</el-radio>
<el-radio label="http">HTTP</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('website.dnsAccount')" prop="dnsAccountId" v-if="ssl.provider === 'dnsAccount'">
<el-select v-model="ssl.dnsAccountId">
<el-option
v-for="(dns, index) in dnsAccounts"
:key="index"
:label="dns.name + ' ( ' + dns.type + ' )'"
:value="dns.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('website.domain')" prop="domains">
<el-checkbox-group v-model="ssl.domains">
<el-checkbox v-for="domain in domains" :key="domain.domain" :label="domain.domain"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<div>
<span>解析域名: {{ dnsResolve.key }}</span>
<span>记录值: {{ dnsResolve.value }}</span>
<span>类型: {{ dnsResolve.type }}</span>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submit(sslForm)" :loading="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { WebSite } from '@/api/interface/website';
import { CreateSSL, GetDnsResolve, GetWebsite, SearchAcmeAccount, SearchDnsAccount } from '@/api/modules/website';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElMessage, FormInstance } from 'element-plus';
import { computed, reactive, ref } from 'vue';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
let open = ref(false);
let loading = ref(false);
let dnsReq = reactive({
page: 1,
pageSize: 20,
});
let acmeReq = reactive({
page: 1,
pageSize: 20,
});
let dnsAccounts = ref<WebSite.DnsAccount[]>();
let acmeAccounts = ref<WebSite.AcmeAccount[]>();
let domains = ref<WebSite.Domain[]>([]);
let sslForm = ref<FormInstance>();
let rules = ref({
acmeAccountId: [Rules.requiredSelectBusiness],
dnsAccountId: [Rules.requiredSelectBusiness],
provider: [Rules.requiredInput],
domains: [Rules.requiredSelect],
});
let ssl = ref({
domains: [],
provider: 'dnsAccount',
websiteId: 0,
acmeAccountId: 0,
dnsAccountId: 0,
});
let dnsResolve = ref<WebSite.DNSResolve>({
key: '',
value: '',
type: '',
});
let hasResolve = ref(false);
const em = defineEmits(['close']);
const handleClose = () => {
resetForm();
open.value = false;
em('close', false);
};
const resetForm = () => {
sslForm.value?.resetFields();
ssl.value = {
domains: [],
provider: 'dnsAccount',
websiteId: 0,
acmeAccountId: 0,
dnsAccountId: 0,
};
};
const acceptParams = () => {
resetForm();
ssl.value.websiteId = Number(id.value);
getWebsite(id.value);
getAcmeAccounts();
getDnsAccounts();
open.value = true;
};
const getAcmeAccounts = async () => {
const res = await SearchAcmeAccount(acmeReq);
acmeAccounts.value = res.data.items || [];
if (acmeAccounts.value.length > 0) {
ssl.value.acmeAccountId = res.data.items[0].id;
}
};
const getDnsAccounts = async () => {
const res = await SearchDnsAccount(dnsReq);
dnsAccounts.value = res.data.items || [];
if (dnsAccounts.value.length > 0) {
ssl.value.dnsAccountId = res.data.items[0].id;
}
};
const getWebsite = async (id: number) => {
domains.value = (await GetWebsite(id)).data.domains || [];
};
const getDnsResolve = async (acmeAccountId: number, domains: string[]) => {
hasResolve.value = false;
const res = await GetDnsResolve({ acmeAccountId: acmeAccountId, domains: domains });
if (res.data) {
dnsResolve.value = res.data;
hasResolve.value = true;
}
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid) => {
if (!valid) {
return;
}
if (ssl.value.provider != 'dnsCommon' || hasResolve.value) {
loading.value = true;
await CreateSSL(ssl.value);
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
loading.value = false;
} else {
getDnsResolve(ssl.value.acmeAccountId, ssl.value.domains);
}
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -2,20 +2,51 @@
<div>
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()">
<template #toolbar>
<el-button type="primary" plain>{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain @click="openSSL()">{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain @click="openAccount()">{{ $t('website.accountManage') }}</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="name"></el-table-column>
<el-table-column :label="$t('website.domain')" fix show-overflow-tooltip prop="domain"></el-table-column>
<el-table-column :label="$t('website.brand')" fix show-overflow-tooltip prop="type"></el-table-column>
<el-table-column
prop="expireDate"
:label="$t('website.expireDate')"
:formatter="dateFromat"
show-overflow-tooltip
/>
<fu-table-operations
:ellipsis="1"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
fix
/>
</ComplexTable>
<Account ref="accountRef"></Account>
<Create :id="id" ref="sslCreateRef"></Create>
</div>
</template>
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import { onMounted, reactive, ref } from 'vue';
import { SearchSSL } from '@/api/modules/website';
import { computed, onMounted, reactive, ref } from 'vue';
import { ApplySSL, DeleteSSL, SearchSSL } from '@/api/modules/website';
import Account from './account/index.vue';
import Create from './create/index.vue';
import { dateFromat } from '@/utils/util';
import i18n from '@/lang';
import { WebSite } from '@/api/interface/website';
import { useDeleteData } from '@/hooks/use-delete-data';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
const paginationConfig = reactive({
currentPage: 1,
@ -23,22 +54,65 @@ const paginationConfig = reactive({
total: 0,
});
const accountRef = ref();
const sslCreateRef = ref();
let data = ref();
let loading = ref(false);
const buttons = [
{
label: i18n.global.t('website.deploySSL'),
click: function (row: WebSite.WebSite) {
applySSL(row.id);
},
},
{
label: i18n.global.t('app.delete'),
click: function (row: WebSite.WebSite) {
deleteSSL(row.id);
},
},
];
const search = () => {
const req = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};
SearchSSL(req).then((res) => {
data.value = res.data.items;
paginationConfig.total = res.data.total;
});
loading.value = true;
SearchSSL(req)
.then((res) => {
data.value = res.data.items || [];
paginationConfig.total = res.data.total;
})
.finally(() => {
loading.value = false;
});
};
const openAccount = () => {
accountRef.value.acceptParams();
};
const openSSL = () => {
sslCreateRef.value.acceptParams();
};
const deleteSSL = async (id: number) => {
loading.value = true;
await useDeleteData(DeleteSSL, id, 'commons.msg.delete', false);
loading.value = false;
search();
};
const applySSL = async (sslId: number) => {
loading.value = true;
const apply = {
websiteId: Number(id.value),
SSLId: sslId,
};
await useDeleteData(ApplySSL, apply, 'website.deploySSLHelper', false);
loading.value = false;
search();
};
onMounted(() => {
search();

2
go.mod
View File

@ -56,12 +56,12 @@ require (
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.9.4 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect

6
go.sum
View File

@ -139,6 +139,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1eXdZyMw3F7uGA9qPn2J4+R8=
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@ -482,6 +484,7 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
@ -655,6 +658,7 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@ -665,6 +669,7 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -1653,6 +1658,7 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=