mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-27 20:49:03 +08:00
feat: 证书增加 ZeroSSL BuyPass Google 申请 (#2951)
Refs https://github.com/1Panel-dev/1Panel/issues/215
This commit is contained in:
parent
ff6ede9429
commit
6301fede62
@ -26,7 +26,10 @@ type WebsiteSSLRenew struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteAcmeAccountCreate struct {
|
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"`
|
||||||
|
EabKid string `json:"eabKid"`
|
||||||
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteDnsAccountCreate struct {
|
type WebsiteDnsAccountCreate struct {
|
||||||
|
@ -5,6 +5,9 @@ type WebsiteAcmeAccount struct {
|
|||||||
Email string `gorm:"type:varchar(256);not null" json:"email"`
|
Email string `gorm:"type:varchar(256);not null" json:"email"`
|
||||||
URL string `gorm:"type:varchar(256);not null" json:"url"`
|
URL string `gorm:"type:varchar(256);not null" json:"url"`
|
||||||
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
|
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
|
||||||
|
Type string `gorm:"type:varchar(64);not null;default:letsencrypt" json:"type"`
|
||||||
|
EabKid string `gorm:"type:varchar(256);" json:"eabKid"`
|
||||||
|
EabHmacKey string `gorm:"type:varchar(256);" json:"eabHmacKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteAcmeAccount) TableName() string {
|
func (w WebsiteAcmeAccount) TableName() string {
|
||||||
|
@ -40,19 +40,28 @@ func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreat
|
|||||||
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
|
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssl.NewAcmeClient(create.Email, "")
|
if create.Type == "google" && (create.EabKid == "" || create.EabHmacKey == "") {
|
||||||
|
return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEabKidOrEabHmacKeyCannotBlank)
|
||||||
|
} else {
|
||||||
|
create.EabKid = ""
|
||||||
|
create.EabHmacKey = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
acmeAccount := &model.WebsiteAcmeAccount{
|
||||||
|
Email: create.Email,
|
||||||
|
Type: create.Type,
|
||||||
|
}
|
||||||
|
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.WebsiteAcmeAccountDTO{}, err
|
return response.WebsiteAcmeAccountDTO{}, err
|
||||||
}
|
}
|
||||||
acmeAccount := model.WebsiteAcmeAccount{
|
acmeAccount.PrivateKey = string(ssl.GetPrivateKey(client.User.GetPrivateKey()))
|
||||||
Email: create.Email,
|
acmeAccount.URL = client.User.Registration.URI
|
||||||
URL: client.User.Registration.URI,
|
|
||||||
PrivateKey: string(ssl.GetPrivateKey(client.User.GetPrivateKey())),
|
if err := websiteAcmeRepo.Create(*acmeAccount); err != nil {
|
||||||
}
|
|
||||||
if err := websiteAcmeRepo.Create(acmeAccount); err != nil {
|
|
||||||
return response.WebsiteAcmeAccountDTO{}, err
|
return response.WebsiteAcmeAccountDTO{}, err
|
||||||
}
|
}
|
||||||
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: acmeAccount}, nil
|
return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteAcmeAccountService) Delete(id uint) error {
|
func (w WebsiteAcmeAccountService) Delete(id uint) error {
|
||||||
|
@ -95,7 +95,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ func (w WebsiteSSLService) Renew(sslId uint) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
client, err := ssl.NewAcmeClient(acmeAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,11 @@ var (
|
|||||||
|
|
||||||
// ssl
|
// ssl
|
||||||
var (
|
var (
|
||||||
ErrSSLCannotDelete = "ErrSSLCannotDelete"
|
ErrSSLCannotDelete = "ErrSSLCannotDelete"
|
||||||
ErrAccountCannotDelete = "ErrAccountCannotDelete"
|
ErrAccountCannotDelete = "ErrAccountCannotDelete"
|
||||||
ErrSSLApply = "ErrSSLApply"
|
ErrSSLApply = "ErrSSLApply"
|
||||||
ErrEmailIsExist = "ErrEmailIsExist"
|
ErrEmailIsExist = "ErrEmailIsExist"
|
||||||
|
ErrEabKidOrEabHmacKeyCannotBlank = "ErrEabKidOrEabHmacKeyCannotBlank"
|
||||||
)
|
)
|
||||||
|
|
||||||
// file
|
// file
|
||||||
|
@ -91,6 +91,7 @@ ErrSSLKeyNotFound: 'The private key file does not exist'
|
|||||||
ErrSSLCertificateNotFound: 'The certificate file does not exist'
|
ErrSSLCertificateNotFound: 'The certificate file does not exist'
|
||||||
ErrSSLKeyFormat: 'Private key file verification error'
|
ErrSSLKeyFormat: 'Private key file verification error'
|
||||||
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
|
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
|
||||||
|
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be empty'
|
||||||
|
|
||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
||||||
|
@ -91,6 +91,7 @@ ErrSSLKeyNotFound: '私鑰文件不存在'
|
|||||||
ErrSSLCertificateNotFound: '證書文件不存在'
|
ErrSSLCertificateNotFound: '證書文件不存在'
|
||||||
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
|
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
|
||||||
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
|
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
|
||||||
|
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
|
||||||
|
|
||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "當前用戶已存在,請重新輸入"
|
ErrUserIsExist: "當前用戶已存在,請重新輸入"
|
||||||
|
@ -91,6 +91,7 @@ ErrSSLKeyNotFound: '私钥文件不存在'
|
|||||||
ErrSSLCertificateNotFound: '证书文件不存在'
|
ErrSSLCertificateNotFound: '证书文件不存在'
|
||||||
ErrSSLKeyFormat: '私钥文件校验失败'
|
ErrSSLKeyFormat: '私钥文件校验失败'
|
||||||
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
||||||
|
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'
|
||||||
|
|
||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "当前用户已存在,请重新输入"
|
ErrUserIsExist: "当前用户已存在,请重新输入"
|
||||||
|
@ -52,6 +52,8 @@ func Init() {
|
|||||||
migrations.AddBindAddress,
|
migrations.AddBindAddress,
|
||||||
migrations.AddCommandGroup,
|
migrations.AddCommandGroup,
|
||||||
migrations.AddAppSyncStatus,
|
migrations.AddAppSyncStatus,
|
||||||
|
|
||||||
|
migrations.UpdateAcmeAccount,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
17
backend/init/migration/migrations/v_1_9.go
Normal file
17
backend/init/migration/migrations/v_1_9.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/go-gormigrate/gormigrate/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var UpdateAcmeAccount = &gormigrate.Migration{
|
||||||
|
ID: "20231115-update-acme-account",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.WebsiteAcmeAccount{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -5,10 +5,16 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
type domainError struct {
|
type domainError struct {
|
||||||
@ -16,6 +22,12 @@ type domainError struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type zeroSSLRes struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
EabKid string `json:"eab_kid"`
|
||||||
|
EabHmacKey string `json:"eab_hmac_key"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetPrivateKey(priKey crypto.PrivateKey) []byte {
|
func GetPrivateKey(priKey crypto.PrivateKey) []byte {
|
||||||
rsaKey := priKey.(*rsa.PrivateKey)
|
rsaKey := priKey.(*rsa.PrivateKey)
|
||||||
derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
|
derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
|
||||||
@ -26,24 +38,64 @@ func GetPrivateKey(priKey crypto.PrivateKey) []byte {
|
|||||||
return pem.EncodeToMemory(block)
|
return pem.EncodeToMemory(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRegisterClient(email string) (*AcmeClient, error) {
|
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
|
||||||
priKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
var (
|
||||||
if err != nil {
|
priKey *rsa.PrivateKey
|
||||||
panic(err)
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if acmeAccount.PrivateKey != "" {
|
||||||
|
block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
|
||||||
|
priKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
priKey, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myUser := &AcmeUser{
|
myUser := &AcmeUser{
|
||||||
Email: email,
|
Email: acmeAccount.Email,
|
||||||
Key: priKey,
|
Key: priKey,
|
||||||
}
|
}
|
||||||
config := newConfig(myUser)
|
config := newConfig(myUser, acmeAccount.Type)
|
||||||
client, err := lego.NewClient(config)
|
client, err := lego.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
var reg *registration.Resource
|
||||||
if err != nil {
|
if acmeAccount.Type == "zerossl" || acmeAccount.Type == "google" {
|
||||||
return nil, err
|
if acmeAccount.Type == "zerossl" {
|
||||||
|
var res *zeroSSLRes
|
||||||
|
res, err = getZeroSSLEabCredentials(acmeAccount.Email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.Success {
|
||||||
|
acmeAccount.EabKid = res.EabKid
|
||||||
|
acmeAccount.EabHmacKey = res.EabHmacKey
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("get zero ssl eab credentials failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eabOptions := registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: acmeAccount.EabKid,
|
||||||
|
HmacEncoded: acmeAccount.EabHmacKey,
|
||||||
|
}
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
myUser.Registration = reg
|
myUser.Registration = reg
|
||||||
|
|
||||||
@ -56,41 +108,58 @@ func NewRegisterClient(email string) (*AcmeClient, error) {
|
|||||||
return acmeClient, nil
|
return acmeClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrivateKeyClient(email string, privateKey string) (*AcmeClient, error) {
|
func newConfig(user *AcmeUser, accountType string) *lego.Config {
|
||||||
block, _ := pem.Decode([]byte(privateKey))
|
|
||||||
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
myUser := &AcmeUser{
|
|
||||||
Email: email,
|
|
||||||
Key: priKey,
|
|
||||||
}
|
|
||||||
config := newConfig(myUser)
|
|
||||||
client, err := lego.NewClient(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
reg, err := client.Registration.ResolveAccountByKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
myUser.Registration = reg
|
|
||||||
|
|
||||||
acmeClient := &AcmeClient{
|
|
||||||
User: myUser,
|
|
||||||
Client: client,
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
return acmeClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConfig(user *AcmeUser) *lego.Config {
|
|
||||||
config := lego.NewConfig(user)
|
config := lego.NewConfig(user)
|
||||||
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
switch accountType {
|
||||||
//config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
case "letsEncrypt":
|
||||||
config.UserAgent = "acm_go/0.0.1"
|
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
case "zeroSSL":
|
||||||
|
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||||
|
case "buyPass":
|
||||||
|
config.CADirURL = "https://api.buypass.com/acme/directory"
|
||||||
|
case "google":
|
||||||
|
config.CADirURL = "https://dv.acme-v02.api.pki.goog/directory"
|
||||||
|
}
|
||||||
|
|
||||||
|
config.UserAgent = "1Panel"
|
||||||
config.Certificate.KeyType = certcrypto.RSA2048
|
config.Certificate.KeyType = certcrypto.RSA2048
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getZeroSSLEabCredentials(email string) (*zeroSSLRes, error) {
|
||||||
|
baseURL := "https://api.zerossl.com/acme/eab-credentials-email"
|
||||||
|
params := url.Values{}
|
||||||
|
params.Add("email", email)
|
||||||
|
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", requestURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("server returned non-200 status: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result zeroSSLRes
|
||||||
|
err = json.Unmarshal(body, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
@ -2,12 +2,17 @@ package ssl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
@ -23,6 +28,8 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
|
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppList struct {
|
type AppList struct {
|
||||||
@ -171,50 +178,11 @@ func TestCreatePrivate(t *testing.T) {
|
|||||||
|
|
||||||
func TestSSL(t *testing.T) {
|
func TestSSL(t *testing.T) {
|
||||||
|
|
||||||
//// 本地ACME测试用
|
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
//httpClient := &http.Client{
|
|
||||||
// Transport: &http.Transport{
|
|
||||||
// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
// }}
|
|
||||||
|
|
||||||
//priKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
//derStream := x509.MarshalPKCS1PrivateKey(priKey)
|
|
||||||
//block := &pem.Block{
|
|
||||||
// Type: "privateKey",
|
|
||||||
// Bytes: derStream,
|
|
||||||
//}
|
|
||||||
//file, err := os.Create("private.key")
|
|
||||||
//if err != nil {
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
//privateByte := pem.EncodeToMemory(block)
|
|
||||||
//
|
|
||||||
//fmt.Println(string(privateByte))
|
|
||||||
//
|
|
||||||
//if err = pem.Encode(file, block); err != nil {
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
|
|
||||||
key, err := os.ReadFile("private.key")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalf("Failed to generate private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
block2, _ := pem.Decode(key)
|
|
||||||
priKey, err := x509.ParsePKCS1PrivateKey(block2.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//block, _ := pem.Decode([]byte("vcCzoue3m0ufCzVx673quKBYQOho4uULpwj_P6tR60Q"))
|
|
||||||
//priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
myUser := AcmeUser{
|
myUser := AcmeUser{
|
||||||
Email: "you2@yours.com",
|
Email: "you2@yours.com",
|
||||||
Key: priKey,
|
Key: priKey,
|
||||||
@ -223,6 +191,7 @@ func TestSSL(t *testing.T) {
|
|||||||
config := lego.NewConfig(&myUser)
|
config := lego.NewConfig(&myUser)
|
||||||
//config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
//config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
|
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||||
config.UserAgent = "acm_go/0.0.1"
|
config.UserAgent = "acm_go/0.0.1"
|
||||||
|
|
||||||
config.Certificate.KeyType = certcrypto.RSA2048
|
config.Certificate.KeyType = certcrypto.RSA2048
|
||||||
@ -238,11 +207,6 @@ func TestSSL(t *testing.T) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//reg, err := client.Registration.ResolveAccountByKey()
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
myUser.Registration = reg
|
myUser.Registration = reg
|
||||||
|
|
||||||
//获取证书
|
//获取证书
|
||||||
@ -256,35 +220,14 @@ func TestSSL(t *testing.T) {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
//申请证书
|
//申请证书
|
||||||
|
ewDomain := "tuxpanel.com"
|
||||||
|
|
||||||
request := certificate.ObtainRequest{
|
request := certificate.ObtainRequest{
|
||||||
Domains: []string{"1panel.cloud"},
|
Domains: []string{ewDomain},
|
||||||
// 证书链
|
// 证书链
|
||||||
Bundle: true,
|
Bundle: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
//dns01.NewDNSProviderManual()
|
|
||||||
|
|
||||||
//dnsPodConfig := dnspod.NewDefaultConfig()
|
|
||||||
//dnsPodConfig.LoginToken = "1,1"
|
|
||||||
//provider, err := dnspod.1(dnsPodConfig)
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//alidnsConfig := alidns.NewDefaultConfig()
|
|
||||||
//alidnsConfig.SecretKey = "1"
|
|
||||||
//alidnsConfig.APIKey = "1"
|
|
||||||
//p, err := alidns.NewDNSProviderConfig(alidnsConfig)
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//p, err := dns01.NewDNSProviderManual()
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
err = client.Challenge.SetDNS01Provider(&manualDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
|
err = client.Challenge.SetDNS01Provider(&manualDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -294,7 +237,7 @@ func TestSSL(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
order, err := core.Orders.New([]string{"1panel.cloud"})
|
order, err := core.Orders.New([]string{ewDomain})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -303,7 +246,6 @@ func TestSSL(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
//core.Challenges.New()
|
|
||||||
|
|
||||||
domain := challenge.GetTargetedDomain(auth)
|
domain := challenge.GetTargetedDomain(auth)
|
||||||
chlng, err := challenge.FindChallenge(challenge.DNS01, auth)
|
chlng, err := challenge.FindChallenge(challenge.DNS01, auth)
|
||||||
@ -373,3 +315,143 @@ func TestSSL(t *testing.T) {
|
|||||||
fmt.Println(cert2)
|
fmt.Println(cert2)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateCSR(privateKey crypto.PrivateKey, domain string) ([]byte, error) {
|
||||||
|
// 创建证书请求的模板
|
||||||
|
template := x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: domain,
|
||||||
|
},
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 CSR
|
||||||
|
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 CSR 编码为 PEM 格式
|
||||||
|
csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
|
||||||
|
|
||||||
|
// 这里可以将 CSR 写入文件或者返回
|
||||||
|
err = os.WriteFile("csr.pem", csrPEM, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return csrPEM, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZeroSSL(t *testing.T) {
|
||||||
|
|
||||||
|
//switch accountType {
|
||||||
|
//case "letsencrypt":
|
||||||
|
// config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
|
//case "zerossl":
|
||||||
|
// config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||||
|
//case "buypass":
|
||||||
|
// config.CADirURL = "https://api.test4.buypass.no/acme/directory"
|
||||||
|
//}
|
||||||
|
|
||||||
|
domain := "1panel.store"
|
||||||
|
acmeServer := "https://acme.zerossl.com/v2/DV90"
|
||||||
|
|
||||||
|
//acmeServer = "https://api.test4.buypass.no/acme/directory"
|
||||||
|
//
|
||||||
|
//acmeServer = "https://api.buypass.com/acme/directory"
|
||||||
|
|
||||||
|
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := AcmeUser{
|
||||||
|
Email: "zhengkunwang123@sina.com",
|
||||||
|
Key: priKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := lego.NewConfig(&user)
|
||||||
|
|
||||||
|
// 设置ACME服务器URL
|
||||||
|
config.CADirURL = acmeServer
|
||||||
|
config.Certificate.KeyType = certcrypto.RSA2048
|
||||||
|
config.UserAgent = "acm_go/0.0.1"
|
||||||
|
|
||||||
|
// 创建ACME客户端
|
||||||
|
client, err := lego.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroSSl
|
||||||
|
|
||||||
|
kid := "xv1T5Ybi4G2otJlbCTVHjg"
|
||||||
|
hmacEncoded := "pc-mEroSmg2quRpVr7e4L5YWzhm5mSDlk4_ivMUBzAkDjbym3Y6g7RLhR1joQH-JP94qy3PEczKVfw7QSLoh8A"
|
||||||
|
|
||||||
|
eabOptions := registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: kid,
|
||||||
|
HmacEncoded: hmacEncoded,
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := client.Registration.RegisterWithExternalAccountBinding(eabOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroSSl
|
||||||
|
|
||||||
|
//reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
//if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
//}
|
||||||
|
|
||||||
|
user.Registration = reg
|
||||||
|
|
||||||
|
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||||
|
cloudflareConfig.AuthEmail = "zhengkunwang123@sina.com"
|
||||||
|
cloudflareConfig.AuthKey = "c29c0d604897ec1c5c7f14746623524bf040a"
|
||||||
|
p, err := cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute)); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请证书
|
||||||
|
pk, err := certcrypto.GeneratePrivateKey(certcrypto.EC256)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
request := certificate.ObtainRequest{
|
||||||
|
Domains: []string{domain},
|
||||||
|
Bundle: true,
|
||||||
|
PrivateKey: pk,
|
||||||
|
}
|
||||||
|
certificates, err := client.Certificate.Obtain(request)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存证书
|
||||||
|
err = os.WriteFile("certificate.crt", certificates.Certificate, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile("private.key", certificates.PrivateKey, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetEABCre(t *testing.T) {
|
||||||
|
res, err := getZeroSSLEabCredentials("zen@11.com")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%v", res)
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ssl
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/go-acme/lego/v4/acme"
|
"github.com/go-acme/lego/v4/acme"
|
||||||
"github.com/go-acme/lego/v4/acme/api"
|
"github.com/go-acme/lego/v4/acme/api"
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
@ -41,23 +42,15 @@ type AcmeClient struct {
|
|||||||
User *AcmeUser
|
User *AcmeUser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAcmeClient(email, privateKey string) (*AcmeClient, error) {
|
func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
|
||||||
if email == "" {
|
if acmeAccount.Email == "" {
|
||||||
return nil, errors.New("email can not blank")
|
return nil, errors.New("email can not blank")
|
||||||
}
|
}
|
||||||
if privateKey == "" {
|
client, err := NewRegisterClient(acmeAccount)
|
||||||
client, err := NewRegisterClient(email)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
} else {
|
|
||||||
client, err := NewPrivateKeyClient(email, privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
}
|
||||||
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type DnsType string
|
type DnsType string
|
||||||
@ -78,22 +71,25 @@ type DNSParam struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
|
func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
|
||||||
var param DNSParam
|
var (
|
||||||
if err := json.Unmarshal([]byte(params), ¶m); err != nil {
|
param DNSParam
|
||||||
|
p challenge.Provider
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = json.Unmarshal([]byte(params), ¶m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var p challenge.Provider
|
switch dnsType {
|
||||||
var err error
|
case DnsPod:
|
||||||
if dnsType == DnsPod {
|
|
||||||
dnsPodConfig := dnspod.NewDefaultConfig()
|
dnsPodConfig := dnspod.NewDefaultConfig()
|
||||||
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
||||||
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
case AliYun:
|
||||||
if dnsType == AliYun {
|
|
||||||
alidnsConfig := alidns.NewDefaultConfig()
|
alidnsConfig := alidns.NewDefaultConfig()
|
||||||
alidnsConfig.SecretKey = param.SecretKey
|
alidnsConfig.SecretKey = param.SecretKey
|
||||||
alidnsConfig.APIKey = param.AccessKey
|
alidnsConfig.APIKey = param.AccessKey
|
||||||
@ -101,8 +97,7 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
case CloudFlare:
|
||||||
if dnsType == CloudFlare {
|
|
||||||
cloudflareConfig := cloudflare.NewDefaultConfig()
|
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||||
cloudflareConfig.AuthEmail = param.Email
|
cloudflareConfig.AuthEmail = param.Email
|
||||||
cloudflareConfig.AuthKey = param.APIkey
|
cloudflareConfig.AuthKey = param.APIkey
|
||||||
|
@ -85,7 +85,7 @@ export const SearchAcmeAccount = (req: ReqPage) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CreateAcmeAccount = (req: Website.AcmeAccountCreate) => {
|
export const CreateAcmeAccount = (req: Website.AcmeAccountCreate) => {
|
||||||
return http.post<Website.AcmeAccount>(`/websites/acme`, req);
|
return http.post<Website.AcmeAccount>(`/websites/acme`, req, TimeoutEnum.T_10M);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteAcmeAccount = (req: Website.DelReq) => {
|
export const DeleteAcmeAccount = (req: Website.DelReq) => {
|
||||||
|
@ -122,3 +122,10 @@ export const Units = [
|
|||||||
{ label: i18n.global.t('commons.units.month'), value: 'M' },
|
{ label: i18n.global.t('commons.units.month'), value: 'M' },
|
||||||
{ label: i18n.global.t('commons.units.year'), value: 'y' },
|
{ label: i18n.global.t('commons.units.year'), value: 'y' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const AcmeAccountTypes = [
|
||||||
|
{ label: "Let's Encrypt", value: 'letsencrypt' },
|
||||||
|
{ label: 'ZeroSSL', value: 'zerossl' },
|
||||||
|
{ label: 'Buypass', value: 'buypass' },
|
||||||
|
{ label: 'Google Cloud', value: 'google' },
|
||||||
|
];
|
||||||
|
@ -1664,6 +1664,7 @@ const message = {
|
|||||||
openrestryHelper:
|
openrestryHelper:
|
||||||
'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',
|
'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',
|
||||||
primaryDomainHelper: 'Support domain name: port',
|
primaryDomainHelper: 'Support domain name: port',
|
||||||
|
acmeAccountType: 'Account Type',
|
||||||
},
|
},
|
||||||
php: {
|
php: {
|
||||||
short_open_tag: 'Short tag support',
|
short_open_tag: 'Short tag support',
|
||||||
|
@ -1577,6 +1577,7 @@ const message = {
|
|||||||
runDirHelper2: '請確保二級運行目錄位於 index 目錄下',
|
runDirHelper2: '請確保二級運行目錄位於 index 目錄下',
|
||||||
openrestryHelper: 'OpenResty 默認 HTTP 端口:{0} HTTPS 端口:{1},可能影響網站域名訪問和 HTTPS 強制跳轉',
|
openrestryHelper: 'OpenResty 默認 HTTP 端口:{0} HTTPS 端口:{1},可能影響網站域名訪問和 HTTPS 強制跳轉',
|
||||||
primaryDomainHelper: '支援網域:port',
|
primaryDomainHelper: '支援網域:port',
|
||||||
|
acmeAccountType: '賬號類型',
|
||||||
},
|
},
|
||||||
php: {
|
php: {
|
||||||
short_open_tag: '短標簽支持',
|
short_open_tag: '短標簽支持',
|
||||||
|
@ -1577,6 +1577,7 @@ const message = {
|
|||||||
runDirHelper2: '请确保二级运行目录位于 index 目录下',
|
runDirHelper2: '请确保二级运行目录位于 index 目录下',
|
||||||
openrestryHelper: 'OpenResty 默认 HTTP 端口:{0} HTTPS 端口 :{1},可能影响网站域名访问和 HTTPS 强制跳转',
|
openrestryHelper: 'OpenResty 默认 HTTP 端口:{0} HTTPS 端口 :{1},可能影响网站域名访问和 HTTPS 强制跳转',
|
||||||
primaryDomainHelper: '支持域名:端口',
|
primaryDomainHelper: '支持域名:端口',
|
||||||
|
acmeAccountType: '账号类型',
|
||||||
},
|
},
|
||||||
php: {
|
php: {
|
||||||
short_open_tag: '短标签支持',
|
short_open_tag: '短标签支持',
|
||||||
|
@ -12,6 +12,24 @@
|
|||||||
<el-form-item :label="$t('website.email')" prop="email">
|
<el-form-item :label="$t('website.email')" prop="email">
|
||||||
<el-input v-model.trim="account.email"></el-input>
|
<el-input v-model.trim="account.email"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('website.acmeAccountType')" prop="type">
|
||||||
|
<el-select v-model="account.type">
|
||||||
|
<el-option
|
||||||
|
v-for="(acme, index) in AcmeAccountTypes"
|
||||||
|
:key="index"
|
||||||
|
:label="acme.label"
|
||||||
|
:value="acme.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="account.type == 'google'">
|
||||||
|
<el-form-item label="EAB kid" prop="eabKid">
|
||||||
|
<el-input v-model.trim="account.eabKid"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="EAB HmacKey" prop="eabHmacKey">
|
||||||
|
<el-input v-model.trim="account.eabHmacKey"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -32,17 +50,26 @@ import { Rules } from '@/global/form-rules';
|
|||||||
import { CreateAcmeAccount } from '@/api/modules/website';
|
import { CreateAcmeAccount } from '@/api/modules/website';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { AcmeAccountTypes } from '@/global/mimetype';
|
||||||
|
|
||||||
let open = ref();
|
const open = ref();
|
||||||
let loading = ref(false);
|
const loading = ref(false);
|
||||||
let accountForm = ref<FormInstance>();
|
const accountForm = ref<FormInstance>();
|
||||||
let rules = ref({
|
const rules = ref({
|
||||||
email: [Rules.requiredInput, Rules.email],
|
email: [Rules.requiredInput, Rules.email],
|
||||||
});
|
type: [Rules.requiredSelect],
|
||||||
let account = ref({
|
eabKid: [Rules.requiredInput],
|
||||||
email: '',
|
eabHmacKey: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initData = () => ({
|
||||||
|
email: '',
|
||||||
|
type: 'letsencrypt',
|
||||||
|
eabKid: '',
|
||||||
|
eabHmacKey: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const account = ref(initData());
|
||||||
const em = defineEmits(['close']);
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
@ -53,9 +80,7 @@ const handleClose = () => {
|
|||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
accountForm.value.resetFields();
|
accountForm.value.resetFields();
|
||||||
account.value = {
|
account.value = initData();
|
||||||
email: '',
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const acceptParams = () => {
|
const acceptParams = () => {
|
||||||
|
@ -3,12 +3,25 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader :header="$t('website.acmeAccountManage')" :back="handleClose" />
|
<DrawerHeader :header="$t('website.acmeAccountManage')" :back="handleClose" />
|
||||||
</template>
|
</template>
|
||||||
<el-alert :title="$t('ssl.acmeHelper')" type="info" :closable="false" style="margin-bottom: 5px" />
|
<div class="mb-1.5">
|
||||||
|
<el-alert :title="$t('ssl.acmeHelper')" type="info" :closable="false" />
|
||||||
|
</div>
|
||||||
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()" v-loading="loading">
|
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()" v-loading="loading">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<el-button type="primary" @click="openCreate">{{ $t('website.addAccount') }}</el-button>
|
<el-button type="primary" @click="openCreate">{{ $t('website.addAccount') }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column :label="$t('website.email')" fix show-overflow-tooltip prop="email"></el-table-column>
|
<el-table-column
|
||||||
|
:label="$t('website.email')"
|
||||||
|
fix
|
||||||
|
show-overflow-tooltip
|
||||||
|
prop="email"
|
||||||
|
min-width="100px"
|
||||||
|
></el-table-column>
|
||||||
|
<el-table-column :label="$t('website.acmeAccountType')" fix show-overflow-tooltip prop="type">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ getAccountType(row.type) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="URL" show-overflow-tooltip prop="url" min-width="300px"></el-table-column>
|
<el-table-column label="URL" show-overflow-tooltip prop="url" min-width="300px"></el-table-column>
|
||||||
<fu-table-operations
|
<fu-table-operations
|
||||||
:ellipsis="1"
|
:ellipsis="1"
|
||||||
@ -32,11 +45,12 @@ import { DeleteAcmeAccount, SearchAcmeAccount } from '@/api/modules/website';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import Create from './create/index.vue';
|
import Create from './create/index.vue';
|
||||||
|
import { AcmeAccountTypes } from '@/global/mimetype';
|
||||||
|
|
||||||
let open = ref(false);
|
const open = ref(false);
|
||||||
let loading = ref(false);
|
const loading = ref(false);
|
||||||
let data = ref();
|
const data = ref();
|
||||||
let createRef = ref();
|
const createRef = ref();
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
cacheSizeKey: 'acme-account-page-size',
|
cacheSizeKey: 'acme-account-page-size',
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
@ -91,6 +105,14 @@ const deleteAccount = async (row: any) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAccountType = (type: string) => {
|
||||||
|
for (const i of AcmeAccountTypes) {
|
||||||
|
if (i.value === type) {
|
||||||
|
return i.label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
acceptParams,
|
acceptParams,
|
||||||
});
|
});
|
||||||
|
@ -104,8 +104,8 @@ import { Website } from '@/api/interface/website';
|
|||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
import SSLUpload from './upload/index.vue';
|
import SSLUpload from './upload/index.vue';
|
||||||
const globalStore = GlobalStore();
|
|
||||||
|
|
||||||
|
const globalStore = GlobalStore();
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
cacheSizeKey: 'ssl-page-size',
|
cacheSizeKey: 'ssl-page-size',
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
15
go.mod
15
go.mod
@ -16,12 +16,12 @@ require (
|
|||||||
github.com/gin-contrib/i18n v0.0.1
|
github.com/gin-contrib/i18n v0.0.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/glebarez/sqlite v1.8.0
|
github.com/glebarez/sqlite v1.8.0
|
||||||
github.com/go-acme/lego/v4 v4.9.0
|
github.com/go-acme/lego/v4 v4.14.2
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
||||||
github.com/go-playground/validator/v10 v10.14.0
|
github.com/go-playground/validator/v10 v10.14.0
|
||||||
github.com/go-sql-driver/mysql v1.7.0
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
github.com/goh-chunlin/go-onedrive v1.1.1
|
github.com/goh-chunlin/go-onedrive v1.1.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/jinzhu/copier v0.3.5
|
github.com/jinzhu/copier v0.3.5
|
||||||
@ -73,11 +73,11 @@ require (
|
|||||||
github.com/bodgit/sevenzip v1.3.0 // indirect
|
github.com/bodgit/sevenzip v1.3.0 // indirect
|
||||||
github.com/bodgit/windows v1.0.0 // indirect
|
github.com/bodgit/windows v1.0.0 // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect
|
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
||||||
github.com/connesc/cipherio v0.2.1 // indirect
|
github.com/connesc/cipherio v0.2.1 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
github.com/containerd/containerd v1.7.7 // indirect
|
github.com/containerd/containerd v1.7.7 // indirect
|
||||||
@ -101,6 +101,7 @@ require (
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/glebarez/go-sqlite v1.21.1 // indirect
|
github.com/glebarez/go-sqlite v1.21.1 // indirect
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
@ -131,7 +132,7 @@ require (
|
|||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
@ -152,7 +153,7 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/miekg/dns v1.1.50 // indirect
|
github.com/miekg/dns v1.1.55 // indirect
|
||||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
@ -221,7 +222,7 @@ require (
|
|||||||
golang.org/x/sync v0.4.0 // indirect
|
golang.org/x/sync v0.4.0 // indirect
|
||||||
golang.org/x/term v0.13.0 // indirect
|
golang.org/x/term v0.13.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.7.0 // indirect
|
golang.org/x/tools v0.10.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||||
|
36
go.sum
36
go.sum
@ -52,7 +52,7 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935
|
|||||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
@ -102,8 +102,8 @@ github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABF
|
|||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
@ -120,8 +120,8 @@ github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+
|
|||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||||
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e h1:Qux+lbuMaRzkQyTdzgtz8MgzPtzmaPQy6DXmxpdxT3U=
|
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e h1:Qux+lbuMaRzkQyTdzgtz8MgzPtzmaPQy6DXmxpdxT3U=
|
||||||
github.com/cloudflare/cloudflare-go v0.49.0 h1:KqJYk/YQ5ZhmyYz1oa4kGDskfF1gVuZfqesaJ/XDLto=
|
github.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=
|
||||||
github.com/cloudflare/cloudflare-go v0.49.0/go.mod h1:h0QgcIZ3qEXwFiwfBO8sQxjVdYsLX+PfD7NFEnANaKg=
|
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
@ -238,13 +238,15 @@ github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0
|
|||||||
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
|
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
|
||||||
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
|
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
|
||||||
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
|
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
|
||||||
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
|
github.com/go-acme/lego/v4 v4.14.2 h1:/D/jqRgLi8Cbk33sLGtu2pX2jEg3bGJWHyV8kFuUHGM=
|
||||||
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
github.com/go-acme/lego/v4 v4.14.2/go.mod h1:kBXxbeTg0x9AgaOYjPSwIeJy3Y33zTz+tMD16O4MO6c=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2 h1:YV4Lc5yMQX8ahVW0ENPq6sPhrhdkGukc6fPRYmZ1R6Y=
|
github.com/go-gormigrate/gormigrate/v2 v2.0.2 h1:YV4Lc5yMQX8ahVW0ENPq6sPhrhdkGukc6fPRYmZ1R6Y=
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2/go.mod h1:vld36QpBTfTzLealsHsmQQJK5lSwJt6wiORv+oFX8/I=
|
github.com/go-gormigrate/gormigrate/v2 v2.0.2/go.mod h1:vld36QpBTfTzLealsHsmQQJK5lSwJt6wiORv+oFX8/I=
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
@ -305,8 +307,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
|||||||
github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=
|
github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=
|
||||||
github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=
|
github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
|
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
|
||||||
@ -428,8 +430,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
|
|||||||
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
@ -542,8 +544,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
|
|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
|
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
|
||||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
@ -797,7 +799,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
@ -840,6 +841,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
@ -969,7 +971,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -1060,12 +1061,11 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
Loading…
Reference in New Issue
Block a user