Merge pull request #218 from fudiwei/feat/huaweicloud-cdn

feat: huaweicloud cdn
main
usual2970 2024-10-20 06:30:48 +08:00 committed by GitHub
commit 17f72eb9cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 577 additions and 83 deletions

View File

@ -76,7 +76,7 @@ go run main.go serve
| :--------: | :----------: | :----------: | ------------------------------------------------------------ | | :--------: | :----------: | :----------: | ------------------------------------------------------------ |
| 阿里云 | √ | √ | 可签发在阿里云注册的域名;可部署到阿里云 OSS、CDN | | 阿里云 | √ | √ | 可签发在阿里云注册的域名;可部署到阿里云 OSS、CDN |
| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 CDN | | 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 CDN |
| 华为云 | √ | | 可签发在华为云注册的域名 | | 华为云 | √ | √ | 可签发在华为云注册的域名;可部署到华为云 CDN |
| 七牛云 | | √ | 可部署到七牛云 CDN | | 七牛云 | | √ | 可部署到七牛云 CDN |
| AWS | √ | | 可签发在 AWS Route53 托管的域名 | | AWS | √ | | 可签发在 AWS Route53 托管的域名 |
| CloudFlare | √ | | 可签发在 CloudFlare 注册的域名CloudFlare 服务自带 SSL 证书 | | CloudFlare | √ | | 可签发在 CloudFlare 注册的域名CloudFlare 服务自带 SSL 证书 |

View File

@ -75,7 +75,7 @@ password1234567890
| :-----------: | :----------: | :--------: | ------------------------------------------------------------------------------------------- | | :-----------: | :----------: | :--------: | ------------------------------------------------------------------------------------------- |
| Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN | | Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN |
| Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud CDN | | Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud CDN |
| Huawei Cloud | √ | | Supports domains registered on Huawei Cloud | | Huawei Cloud | √ | √ | Supports domains registered on Huawei; supports deployment to Huawei Cloud CDN |
| Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN | | Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN |
| AWS | √ | | Supports domains managed on AWS Route53 | | AWS | √ | | Supports domains managed on AWS Route53 |
| CloudFlare | √ | | Supports domains registered on CloudFlare; CloudFlare services come with SSL certificates | | CloudFlare | √ | | Supports domains registered on CloudFlare; CloudFlare services come with SSL certificates |

2
go.mod
View File

@ -12,6 +12,7 @@ require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/go-acme/lego/v4 v4.19.2 github.com/go-acme/lego/v4 v4.19.2
github.com/gojek/heimdall/v7 v7.0.3 github.com/gojek/heimdall/v7 v7.0.3
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
github.com/nikoksr/notify v1.0.0 github.com/nikoksr/notify v1.0.0
github.com/pkg/sftp v1.13.6 github.com/pkg/sftp v1.13.6
@ -46,7 +47,6 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
github.com/imdario/mergo v0.3.6 // indirect github.com/imdario/mergo v0.3.6 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect

View File

@ -1,8 +1,6 @@
package applicant package applicant
import ( import (
"certimate/internal/domain"
"certimate/internal/utils/app"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@ -18,12 +16,15 @@ import (
"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"
"github.com/pocketbase/pocketbase/models" "github.com/pocketbase/pocketbase/models"
"certimate/internal/domain"
"certimate/internal/utils/app"
) )
const ( const (
configTypeAliyun = "aliyun" configTypeAliyun = "aliyun"
configTypeTencent = "tencent" configTypeTencent = "tencent"
configTypeHuaweicloud = "huaweicloud" configTypeHuaweiCloud = "huaweicloud"
configTypeAws = "aws" configTypeAws = "aws"
configTypeCloudflare = "cloudflare" configTypeCloudflare = "cloudflare"
configTypeNamesilo = "namesilo" configTypeNamesilo = "namesilo"
@ -125,7 +126,7 @@ func Get(record *models.Record) (Applicant, error) {
return NewAliyun(option), nil return NewAliyun(option), nil
case configTypeTencent: case configTypeTencent:
return NewTencent(option), nil return NewTencent(option), nil
case configTypeHuaweicloud: case configTypeHuaweiCloud:
return NewHuaweiCloud(option), nil return NewHuaweiCloud(option), nil
case configTypeAws: case configTypeAws:
return NewAws(option), nil return NewAws(option), nil

View File

@ -20,7 +20,7 @@ type AliyunCDNDeployer struct {
infos []string infos []string
} }
func NewAliyunCdnDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) { func NewAliyunCDNDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
access := &domain.AliyunAccess{} access := &domain.AliyunAccess{}
json.Unmarshal([]byte(option.Access), access) json.Unmarshal([]byte(option.Access), access)
@ -41,7 +41,7 @@ func NewAliyunCdnDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
} }
func (d *AliyunCDNDeployer) GetID() string { func (d *AliyunCDNDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *AliyunCDNDeployer) GetInfo() []string { func (d *AliyunCDNDeployer) GetInfo() []string {

View File

@ -25,7 +25,7 @@ type AliyunESADeployer struct {
infos []string infos []string
} }
func NewAliyunEsaDeployer(option *DeployerOption) (*AliyunESADeployer, error) { func NewAliyunESADeployer(option *DeployerOption) (*AliyunESADeployer, error) {
access := &domain.AliyunAccess{} access := &domain.AliyunAccess{}
json.Unmarshal([]byte(option.Access), access) json.Unmarshal([]byte(option.Access), access)
@ -46,7 +46,7 @@ func NewAliyunEsaDeployer(option *DeployerOption) (*AliyunESADeployer, error) {
} }
func (d *AliyunESADeployer) GetID() string { func (d *AliyunESADeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *AliyunESADeployer) GetInfo() []string { func (d *AliyunESADeployer) GetInfo() []string {

View File

@ -16,7 +16,7 @@ type AliyunOSSDeployer struct {
infos []string infos []string
} }
func NewAliyunOssDeployer(option *DeployerOption) (Deployer, error) { func NewAliyunOSSDeployer(option *DeployerOption) (Deployer, error) {
access := &domain.AliyunAccess{} access := &domain.AliyunAccess{}
json.Unmarshal([]byte(option.Access), access) json.Unmarshal([]byte(option.Access), access)
@ -35,7 +35,7 @@ func NewAliyunOssDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *AliyunOSSDeployer) GetID() string { func (d *AliyunOSSDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *AliyunOSSDeployer) GetInfo() []string { func (d *AliyunOSSDeployer) GetInfo() []string {

View File

@ -15,15 +15,16 @@ import (
) )
const ( const (
targetAliyunOSS = "aliyun-oss" targetAliyunOSS = "aliyun-oss"
targetAliyunCDN = "aliyun-cdn" targetAliyunCDN = "aliyun-cdn"
targetAliyunESA = "aliyun-dcdn" targetAliyunESA = "aliyun-dcdn"
targetTencentCDN = "tencent-cdn" targetTencentCDN = "tencent-cdn"
targetQiniuCdn = "qiniu-cdn" targetHuaweiCloudCDN = "huaweicloud-cdn"
targetLocal = "local" targetQiniuCdn = "qiniu-cdn"
targetSSH = "ssh" targetLocal = "local"
targetWebhook = "webhook" targetSSH = "ssh"
targetK8sSecret = "k8s-secret" targetWebhook = "webhook"
targetK8sSecret = "k8s-secret"
) )
type DeployerOption struct { type DeployerOption struct {
@ -31,7 +32,7 @@ type DeployerOption struct {
Domain string `json:"domain"` Domain string `json:"domain"`
Product string `json:"product"` Product string `json:"product"`
Access string `json:"access"` Access string `json:"access"`
AceessRecord *models.Record `json:"-"` AccessRecord *models.Record `json:"-"`
DeployConfig domain.DeployConfig `json:"deployConfig"` DeployConfig domain.DeployConfig `json:"deployConfig"`
Certificate applicant.Certificate `json:"certificate"` Certificate applicant.Certificate `json:"certificate"`
Variables map[string]string `json:"variables"` Variables map[string]string `json:"variables"`
@ -83,7 +84,7 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
Domain: record.GetString("domain"), Domain: record.GetString("domain"),
Product: getProduct(deployConfig.Type), Product: getProduct(deployConfig.Type),
Access: access.GetString("config"), Access: access.GetString("config"),
AceessRecord: access, AccessRecord: access,
DeployConfig: deployConfig, DeployConfig: deployConfig,
} }
if cert != nil { if cert != nil {
@ -97,13 +98,15 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
switch deployConfig.Type { switch deployConfig.Type {
case targetAliyunOSS: case targetAliyunOSS:
return NewAliyunOssDeployer(option) return NewAliyunOSSDeployer(option)
case targetAliyunCDN: case targetAliyunCDN:
return NewAliyunCdnDeployer(option) return NewAliyunCDNDeployer(option)
case targetAliyunESA: case targetAliyunESA:
return NewAliyunEsaDeployer(option) return NewAliyunESADeployer(option)
case targetTencentCDN: case targetTencentCDN:
return NewTencentCDNDeployer(option) return NewTencentCDNDeployer(option)
case targetHuaweiCloudCDN:
return NewHuaweiCloudCDNDeployer(option)
case targetQiniuCdn: case targetQiniuCdn:
return NewQiniuCDNDeployer(option) return NewQiniuCDNDeployer(option)
case targetLocal: case targetLocal:

View File

@ -0,0 +1,150 @@
package deployer
import (
"context"
"encoding/json"
"fmt"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
cdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
cdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
cdnRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region"
"certimate/internal/domain"
"certimate/internal/utils/rand"
)
type HuaweiCloudCDNDeployer struct {
option *DeployerOption
infos []string
}
func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
return &HuaweiCloudCDNDeployer{
option: option,
infos: make([]string, 0),
}, nil
}
func (d *HuaweiCloudCDNDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
}
func (d *HuaweiCloudCDNDeployer) GetInfo() []string {
return d.infos
}
func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
access := &domain.HuaweiCloudAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return err
}
client, err := d.createClient(access)
if err != nil {
return err
}
d.infos = append(d.infos, toStr("HuaweiCloudCdnClient 创建成功", nil))
// 查询加速域名配置
showDomainFullConfigReq := &cdnModel.ShowDomainFullConfigRequest{
DomainName: getDeployString(d.option.DeployConfig, "domain"),
}
showDomainFullConfigResp, err := client.ShowDomainFullConfig(showDomainFullConfigReq)
if err != nil {
return err
}
d.infos = append(d.infos, toStr("已查询到加速域名配置", showDomainFullConfigResp))
// 更新加速域名配置
certName := fmt.Sprintf("%s-%s", d.option.DomainId, rand.RandStr(12))
updateDomainMultiCertificatesReq := &cdnModel.UpdateDomainMultiCertificatesRequest{
Body: &cdnModel.UpdateDomainMultiCertificatesRequestBody{
Https: mergeHuaweiCloudCDNConfig(showDomainFullConfigResp.Configs, &cdnModel.UpdateDomainMultiCertificatesRequestBodyContent{
DomainName: getDeployString(d.option.DeployConfig, "domain"),
HttpsSwitch: 1,
CertName: &certName,
Certificate: &d.option.Certificate.Certificate,
PrivateKey: &d.option.Certificate.PrivateKey,
}),
},
}
updateDomainMultiCertificatesResp, err := client.UpdateDomainMultiCertificates(updateDomainMultiCertificatesReq)
if err != nil {
return err
}
d.infos = append(d.infos, toStr("已更新加速域名配置", updateDomainMultiCertificatesResp))
return nil
}
func (d *HuaweiCloudCDNDeployer) createClient(access *domain.HuaweiCloudAccess) (*cdn.CdnClient, error) {
auth, err := global.NewCredentialsBuilder().
WithAk(access.AccessKeyId).
WithSk(access.SecretAccessKey).
SafeBuild()
if err != nil {
return nil, err
}
region, err := cdnRegion.SafeValueOf(access.Region)
if err != nil {
return nil, err
}
hcClient, err := cdn.CdnClientBuilder().
WithRegion(region).
WithCredential(auth).
SafeBuild()
if err != nil {
return nil, err
}
client := cdn.NewCdnClient(hcClient)
return client, nil
}
func mergeHuaweiCloudCDNConfig(src *cdnModel.ConfigsGetBody, dest *cdnModel.UpdateDomainMultiCertificatesRequestBodyContent) *cdnModel.UpdateDomainMultiCertificatesRequestBodyContent {
if src == nil {
return dest
}
// 华为云 API 中不传的字段表示使用默认值、而非保留原值,因此这里需要把原配置中的参数重新赋值回去
// 而且蛋疼的是查询接口返回的数据结构和更新接口传入的参数结构不一致,需要做很多转化
// REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html
// REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html
if *src.OriginProtocol == "follow" {
accessOriginWay := int32(1)
dest.AccessOriginWay = &accessOriginWay
} else if *src.OriginProtocol == "http" {
accessOriginWay := int32(2)
dest.AccessOriginWay = &accessOriginWay
} else if *src.OriginProtocol == "https" {
accessOriginWay := int32(3)
dest.AccessOriginWay = &accessOriginWay
}
if src.ForceRedirect != nil {
dest.ForceRedirectConfig = &cdnModel.ForceRedirect{}
if src.ForceRedirect.Status == "on" {
dest.ForceRedirectConfig.Switch = 1
dest.ForceRedirectConfig.RedirectType = src.ForceRedirect.Type
} else {
dest.ForceRedirectConfig.Switch = 0
}
}
if src.Https != nil {
if *src.Https.Http2Status == "on" {
http2 := int32(1)
dest.Http2 = &http2
}
}
return dest
}

View File

@ -8,11 +8,9 @@ import (
k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
)
type KubernetesAccess struct { "certimate/internal/domain"
KubeConfig string `json:"kubeConfig"` )
}
type K8sSecretDeployer struct { type K8sSecretDeployer struct {
option *DeployerOption option *DeployerOption
@ -27,7 +25,7 @@ func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *K8sSecretDeployer) GetID() string { func (d *K8sSecretDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *K8sSecretDeployer) GetInfo() []string { func (d *K8sSecretDeployer) GetInfo() []string {
@ -35,7 +33,7 @@ func (d *K8sSecretDeployer) GetInfo() []string {
} }
func (d *K8sSecretDeployer) Deploy(ctx context.Context) error { func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
access := &KubernetesAccess{} access := &domain.KubernetesAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return err return err
} }
@ -86,7 +84,7 @@ func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
return nil return nil
} }
func (d *K8sSecretDeployer) createClient(access *KubernetesAccess) (*kubernetes.Clientset, error) { func (d *K8sSecretDeployer) createClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) {
kubeConfig, err := clientcmd.Load([]byte(access.KubeConfig)) kubeConfig, err := clientcmd.Load([]byte(access.KubeConfig))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -8,9 +8,9 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
)
type LocalAccess struct{} "certimate/internal/domain"
)
type LocalDeployer struct { type LocalDeployer struct {
option *DeployerOption option *DeployerOption
@ -25,7 +25,7 @@ func NewLocalDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *LocalDeployer) GetID() string { func (d *LocalDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *LocalDeployer) GetInfo() []string { func (d *LocalDeployer) GetInfo() []string {
@ -33,7 +33,7 @@ func (d *LocalDeployer) GetInfo() []string {
} }
func (d *LocalDeployer) Deploy(ctx context.Context) error { func (d *LocalDeployer) Deploy(ctx context.Context) error {
access := &LocalAccess{} access := &domain.LocalAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return err return err
} }

View File

@ -35,7 +35,7 @@ func NewQiniuCDNDeployer(option *DeployerOption) (*QiniuCDNDeployer, error) {
} }
func (d *QiniuCDNDeployer) GetID() string { func (d *QiniuCDNDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *QiniuCDNDeployer) GetInfo() []string { func (d *QiniuCDNDeployer) GetInfo() []string {

View File

@ -10,16 +10,9 @@ import (
"github.com/pkg/sftp" "github.com/pkg/sftp"
sshPkg "golang.org/x/crypto/ssh" sshPkg "golang.org/x/crypto/ssh"
)
type SSHAccess struct { "certimate/internal/domain"
Host string `json:"host"` )
Port string `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Key string `json:"key"`
KeyPassphrase string `json:"keyPassphrase"`
}
type SSHDeployer struct { type SSHDeployer struct {
option *DeployerOption option *DeployerOption
@ -34,7 +27,7 @@ func NewSSHDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *SSHDeployer) GetID() string { func (d *SSHDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *SSHDeployer) GetInfo() []string { func (d *SSHDeployer) GetInfo() []string {
@ -42,7 +35,7 @@ func (d *SSHDeployer) GetInfo() []string {
} }
func (d *SSHDeployer) Deploy(ctx context.Context) error { func (d *SSHDeployer) Deploy(ctx context.Context) error {
access := &SSHAccess{} access := &domain.SSHAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return err return err
} }
@ -130,7 +123,7 @@ func (d *SSHDeployer) upload(client *sshPkg.Client, content, path string) error
return nil return nil
} }
func (d *SSHDeployer) createClient(access *SSHAccess) (*sshPkg.Client, error) { func (d *SSHDeployer) createClient(access *domain.SSHAccess) (*sshPkg.Client, error) {
var authMethod sshPkg.AuthMethod var authMethod sshPkg.AuthMethod
if access.Key != "" { if access.Key != "" {

View File

@ -41,7 +41,7 @@ func NewTencentCDNDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *TencentCDNDeployer) GetID() string { func (d *TencentCDNDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *TencentCDNDeployer) GetInfo() []string { func (d *TencentCDNDeployer) GetInfo() []string {

View File

@ -7,13 +7,10 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"certimate/internal/domain"
xhttp "certimate/internal/utils/http" xhttp "certimate/internal/utils/http"
) )
type WebhookAccess struct {
Url string `json:"url"`
}
type WebhookDeployer struct { type WebhookDeployer struct {
option *DeployerOption option *DeployerOption
infos []string infos []string
@ -27,7 +24,7 @@ func NewWebhookDeployer(option *DeployerOption) (Deployer, error) {
} }
func (d *WebhookDeployer) GetID() string { func (d *WebhookDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id) return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
} }
func (d *WebhookDeployer) GetInfo() []string { func (d *WebhookDeployer) GetInfo() []string {
@ -42,7 +39,7 @@ type webhookData struct {
} }
func (d *WebhookDeployer) Deploy(ctx context.Context) error { func (d *WebhookDeployer) Deploy(ctx context.Context) error {
access := &WebhookAccess{} access := &domain.WebhookAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return fmt.Errorf("failed to parse hook access config: %w", err) return fmt.Errorf("failed to parse hook access config: %w", err)
} }

View File

@ -40,3 +40,22 @@ type GodaddyAccess struct {
ApiKey string `json:"apiKey"` ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"` ApiSecret string `json:"apiSecret"`
} }
type LocalAccess struct{}
type SSHAccess struct {
Host string `json:"host"`
Port string `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Key string `json:"key"`
KeyPassphrase string `json:"keyPassphrase"`
}
type WebhookAccess struct {
Url string `json:"url"`
}
type KubernetesAccess struct {
KubeConfig string `json:"kubeConfig"`
}

329
ui/dist/assets/index-tBXwi-8W.js vendored Normal file

File diff suppressed because one or more lines are too long

29
ui/dist/index.html vendored
View File

@ -1,14 +1,15 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title> <title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-BYO3zdEX.js"></script> <script type="module" crossorigin src="/assets/index-BYO3zdEX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css"> <link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
</head> </head>
<body class="bg-background"> <body class="bg-background">
<div id="root"></div> <div id="root"></div>
</body>
</html> </body>
</html>

View File

@ -8,7 +8,7 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
import AccessAliyunForm from "./AccessAliyunForm"; import AccessAliyunForm from "./AccessAliyunForm";
import AccessTencentForm from "./AccessTencentForm"; import AccessTencentForm from "./AccessTencentForm";
import AccessHuaweicloudForm from "./AccessHuaweicloudForm"; import AccessHuaweiCloudForm from "./AccessHuaweicloudForm";
import AccessQiniuForm from "./AccessQiniuForm"; import AccessQiniuForm from "./AccessQiniuForm";
import AccessAwsForm from "./AccessAwsForm"; import AccessAwsForm from "./AccessAwsForm";
import AccessCloudflareForm from "./AccessCloudflareForm"; import AccessCloudflareForm from "./AccessCloudflareForm";
@ -61,7 +61,7 @@ const AccessEdit = ({ trigger, op, data, className }: AccessEditProps) => {
break; break;
case "huaweicloud": case "huaweicloud":
form = ( form = (
<AccessHuaweicloudForm <AccessHuaweiCloudForm
data={data} data={data}
op={op} op={op}
onAfterReq={() => { onAfterReq={() => {

View File

@ -8,17 +8,17 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { PbErrorData } from "@/domain/base"; import { PbErrorData } from "@/domain/base";
import { Access, accessFormType, HuaweicloudConfig, getUsageByConfigType } from "@/domain/access"; import { Access, accessFormType, HuaweiCloudConfig, getUsageByConfigType } from "@/domain/access";
import { save } from "@/repository/access"; import { save } from "@/repository/access";
import { useConfig } from "@/providers/config"; import { useConfig } from "@/providers/config";
type AccessHuaweicloudFormProps = { type AccessHuaweiCloudFormProps = {
op: "add" | "edit" | "copy"; op: "add" | "edit" | "copy";
data?: Access; data?: Access;
onAfterReq: () => void; onAfterReq: () => void;
}; };
const AccessHuaweicloudForm = ({ data, op, onAfterReq }: AccessHuaweicloudFormProps) => { const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormProps) => {
const { addAccess, updateAccess } = useConfig(); const { addAccess, updateAccess } = useConfig();
const { t } = useTranslation(); const { t } = useTranslation();
const formSchema = z.object({ const formSchema = z.object({
@ -42,12 +42,12 @@ const AccessHuaweicloudForm = ({ data, op, onAfterReq }: AccessHuaweicloudFormPr
.max(64, t("common.errmsg.string_max", { max: 64 })), .max(64, t("common.errmsg.string_max", { max: 64 })),
}); });
let config: HuaweicloudConfig = { let config: HuaweiCloudConfig = {
region: "cn-north-1", region: "cn-north-1",
accessKeyId: "", accessKeyId: "",
secretAccessKey: "", secretAccessKey: "",
}; };
if (data) config = data.config as HuaweicloudConfig; if (data) config = data.config as HuaweiCloudConfig;
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
@ -215,4 +215,4 @@ const AccessHuaweicloudForm = ({ data, op, onAfterReq }: AccessHuaweicloudFormPr
); );
}; };
export default AccessHuaweicloudForm; export default AccessHuaweiCloudForm;

View File

@ -48,7 +48,7 @@ export type Access = {
config: config:
| AliyunConfig | AliyunConfig
| TencentConfig | TencentConfig
| HuaweicloudConfig | HuaweiCloudConfig
| QiniuConfig | QiniuConfig
| AwsConfig | AwsConfig
| CloudflareConfig | CloudflareConfig
@ -73,7 +73,7 @@ export type TencentConfig = {
secretKey: string; secretKey: string;
}; };
export type HuaweicloudConfig = { export type HuaweiCloudConfig = {
region: string; region: string;
accessKeyId: string; accessKeyId: string;
secretAccessKey: string; secretAccessKey: string;

View File

@ -71,6 +71,7 @@ export const targetTypeMap: Map<string, [string, string]> = new Map([
["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]], ["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]],
["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]], ["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]], ["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]],
["huaweicloud-cdn", ["common.provider.huaweicloud.cdn", "/imgs/providers/huaweicloud.svg"]],
["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]], ["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]],
["local", ["common.provider.local", "/imgs/providers/local.svg"]], ["local", ["common.provider.local", "/imgs/providers/local.svg"]],
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]], ["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],

View File

@ -59,6 +59,7 @@
"common.provider.tencent": "Tencent", "common.provider.tencent": "Tencent",
"common.provider.tencent.cdn": "Tencent - CDN", "common.provider.tencent.cdn": "Tencent - CDN",
"common.provider.huaweicloud": "Huawei Cloud", "common.provider.huaweicloud": "Huawei Cloud",
"common.provider.huaweicloud.cdn": "Huawei Cloud - CDN",
"common.provider.qiniu": "Qiniu", "common.provider.qiniu": "Qiniu",
"common.provider.qiniu.cdn": "Qiniu - CDN", "common.provider.qiniu.cdn": "Qiniu - CDN",
"common.provider.aws": "AWS", "common.provider.aws": "AWS",

View File

@ -13,7 +13,7 @@
"domain.deploy.started.tips": "Deployment initiated, please check the deployment log later.", "domain.deploy.started.tips": "Deployment initiated, please check the deployment log later.",
"domain.deploy.failed.message": "Execution Failed", "domain.deploy.failed.message": "Execution Failed",
"domain.deploy.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.", "domain.deploy.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
"domain.deploy_forced": "Force Deployment", "domain.deploy_forced": "Force Deploy",
"domain.props.expiry": "Validity Period", "domain.props.expiry": "Validity Period",
"domain.props.expiry.date1": "Valid for {{date}} days", "domain.props.expiry.date1": "Valid for {{date}} days",

View File

@ -59,6 +59,7 @@
"common.provider.aliyun.cdn": "阿里云 - CDN", "common.provider.aliyun.cdn": "阿里云 - CDN",
"common.provider.aliyun.dcdn": "阿里云 - DCDN", "common.provider.aliyun.dcdn": "阿里云 - DCDN",
"common.provider.huaweicloud": "华为云", "common.provider.huaweicloud": "华为云",
"common.provider.huaweicloud.cdn": "华为云 - CDN",
"common.provider.qiniu": "七牛云", "common.provider.qiniu": "七牛云",
"common.provider.qiniu.cdn": "七牛云 - CDN", "common.provider.qiniu.cdn": "七牛云 - CDN",
"common.provider.aws": "AWS", "common.provider.aws": "AWS",