mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
Merge pull request #643 from fudiwei/main
Support configuring independent notification channel for each workflow
This commit is contained in:
commit
fc064aa9f8
2
go.mod
2
go.mod
@ -187,7 +187,7 @@ require (
|
|||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
github.com/spf13/cobra v1.9.1 // indirect
|
github.com/spf13/cobra v1.9.1 // indirect
|
||||||
|
@ -3,12 +3,12 @@ package applicant
|
|||||||
import "github.com/usual2970/certimate/internal/domain"
|
import "github.com/usual2970/certimate/internal/domain"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sslProviderLetsEncrypt = string(domain.ApplyCAProviderTypeLetsEncrypt)
|
sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
|
||||||
sslProviderLetsEncryptStaging = string(domain.ApplyCAProviderTypeLetsEncryptStaging)
|
sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
|
||||||
sslProviderBuypass = string(domain.ApplyCAProviderTypeBuypass)
|
sslProviderBuypass = string(domain.CAProviderTypeBuypass)
|
||||||
sslProviderGoogleTrustServices = string(domain.ApplyCAProviderTypeGoogleTrustServices)
|
sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
|
||||||
sslProviderSSLCom = string(domain.ApplyCAProviderTypeSSLCom)
|
sslProviderSSLCom = string(domain.CAProviderTypeSSLCom)
|
||||||
sslProviderZeroSSL = string(domain.ApplyCAProviderTypeZeroSSL)
|
sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL)
|
||||||
|
|
||||||
sslProviderDefault = sslProviderLetsEncrypt
|
sslProviderDefault = sslProviderLetsEncrypt
|
||||||
)
|
)
|
||||||
@ -25,6 +25,6 @@ var sslProviderUrls = map[string]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type acmeSSLProviderConfig struct {
|
type acmeSSLProviderConfig struct {
|
||||||
Config map[domain.ApplyCAProviderType]map[string]any `json:"config"`
|
Config map[domain.CAProviderType]map[string]any `json:"config"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -22,7 +23,7 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyCertResult struct {
|
type ApplyResult struct {
|
||||||
CertificateFullChain string
|
CertificateFullChain string
|
||||||
IssuerCertificate string
|
IssuerCertificate string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
@ -33,40 +34,30 @@ type ApplyCertResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Applicant interface {
|
type Applicant interface {
|
||||||
Apply() (*ApplyCertResult, error)
|
Apply(ctx context.Context) (*ApplyResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type applicantOptions struct {
|
type ApplicantWithWorkflowNodeConfig struct {
|
||||||
Domains []string
|
Node *domain.WorkflowNode
|
||||||
ContactEmail string
|
Logger *slog.Logger
|
||||||
Provider domain.ApplyDNSProviderType
|
|
||||||
ProviderAccessConfig map[string]any
|
|
||||||
ProviderExtendedConfig map[string]any
|
|
||||||
CAProvider domain.ApplyCAProviderType
|
|
||||||
CAProviderAccessConfig map[string]any
|
|
||||||
CAProviderExtendedConfig map[string]any
|
|
||||||
KeyAlgorithm string
|
|
||||||
Nameservers []string
|
|
||||||
DnsPropagationTimeout int32
|
|
||||||
DnsTTL int32
|
|
||||||
DisableFollowCNAME bool
|
|
||||||
ReplacedARIAcct string
|
|
||||||
ReplacedARICert string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, error) {
|
||||||
if node.Type != domain.WorkflowNodeTypeApply {
|
if config.Node == nil {
|
||||||
|
return nil, fmt.Errorf("node is nil")
|
||||||
|
}
|
||||||
|
if config.Node.Type != domain.WorkflowNodeTypeApply {
|
||||||
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply))
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply))
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig := node.GetConfigForApply()
|
nodeConfig := config.Node.GetConfigForApply()
|
||||||
options := &applicantOptions{
|
options := &applicantProviderOptions{
|
||||||
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
||||||
ContactEmail: nodeConfig.ContactEmail,
|
ContactEmail: nodeConfig.ContactEmail,
|
||||||
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
|
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
|
||||||
ProviderAccessConfig: make(map[string]any),
|
ProviderAccessConfig: make(map[string]any),
|
||||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
CAProvider: domain.ApplyCAProviderType(nodeConfig.CAProvider),
|
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
|
||||||
CAProviderAccessConfig: make(map[string]any),
|
CAProviderAccessConfig: make(map[string]any),
|
||||||
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
|
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
|
||||||
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
||||||
@ -97,7 +88,7 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
|||||||
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
|
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
|
||||||
|
|
||||||
sslProviderConfig := &acmeSSLProviderConfig{
|
sslProviderConfig := &acmeSSLProviderConfig{
|
||||||
Config: make(map[domain.ApplyCAProviderType]map[string]any),
|
Config: make(map[domain.CAProviderType]map[string]any),
|
||||||
Provider: sslProviderDefault,
|
Provider: sslProviderDefault,
|
||||||
}
|
}
|
||||||
if settings != nil {
|
if settings != nil {
|
||||||
@ -108,12 +99,12 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.CAProvider = domain.ApplyCAProviderType(sslProviderConfig.Provider)
|
options.CAProvider = domain.CAProviderType(sslProviderConfig.Provider)
|
||||||
options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider]
|
options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider]
|
||||||
}
|
}
|
||||||
|
|
||||||
certRepo := repository.NewCertificateRepository()
|
certRepo := repository.NewCertificateRepository()
|
||||||
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), node.Id)
|
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id)
|
||||||
if lastCertificate != nil {
|
if lastCertificate != nil {
|
||||||
newCertSan := slices.Clone(options.Domains)
|
newCertSan := slices.Clone(options.Domains)
|
||||||
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
|
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
|
||||||
@ -130,18 +121,46 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := createApplicant(options)
|
applicant, err := createApplicantProvider(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyApplicant{
|
return &applicantImpl{
|
||||||
applicant: applicant,
|
applicant: applicant,
|
||||||
options: options,
|
options: options,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) {
|
type applicantImpl struct {
|
||||||
|
applicant challenge.Provider
|
||||||
|
options *applicantProviderOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Applicant = (*applicantImpl)(nil)
|
||||||
|
|
||||||
|
func (d *applicantImpl) Apply(ctx context.Context) (*ApplyResult, error) {
|
||||||
|
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
|
||||||
|
if err := limiter.Wait(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyUseLego(d.applicant, d.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
limitBurst = 300
|
||||||
|
limitRate float64 = float64(1) / float64(36)
|
||||||
|
)
|
||||||
|
|
||||||
|
var limiters sync.Map
|
||||||
|
|
||||||
|
func getLimiter(key string) *rate.Limiter {
|
||||||
|
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
|
||||||
|
return limiter.(*rate.Limiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
|
||||||
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
|
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -153,7 +172,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
|
|
||||||
// Create an ACME client config
|
// Create an ACME client config
|
||||||
config := lego.NewConfig(user)
|
config := lego.NewConfig(user)
|
||||||
config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
||||||
config.CADirURL = sslProviderUrls[user.CA]
|
config.CADirURL = sslProviderUrls[user.CA]
|
||||||
if user.CA == sslProviderSSLCom {
|
if user.CA == sslProviderSSLCom {
|
||||||
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
|
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
|
||||||
@ -175,7 +194,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
|
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
|
||||||
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
|
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
|
||||||
}
|
}
|
||||||
client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...)
|
client.Challenge.SetDNS01Provider(legoProvider, challengeOptions...)
|
||||||
|
|
||||||
// New users need to register first
|
// New users need to register first
|
||||||
if !user.hasRegistration() {
|
if !user.hasRegistration() {
|
||||||
@ -199,7 +218,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ApplyCertResult{
|
return &ApplyResult{
|
||||||
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
|
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
|
||||||
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
||||||
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
||||||
@ -210,47 +229,20 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
|
func parseLegoKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
|
||||||
switch algo {
|
alogMap := map[domain.CertificateKeyAlgorithmType]certcrypto.KeyType{
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA2048:
|
domain.CertificateKeyAlgorithmTypeRSA2048: certcrypto.RSA2048,
|
||||||
return certcrypto.RSA2048
|
domain.CertificateKeyAlgorithmTypeRSA3072: certcrypto.RSA3072,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA3072:
|
domain.CertificateKeyAlgorithmTypeRSA4096: certcrypto.RSA4096,
|
||||||
return certcrypto.RSA3072
|
domain.CertificateKeyAlgorithmTypeRSA8192: certcrypto.RSA8192,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA4096:
|
domain.CertificateKeyAlgorithmTypeEC256: certcrypto.EC256,
|
||||||
return certcrypto.RSA4096
|
domain.CertificateKeyAlgorithmTypeEC384: certcrypto.EC384,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA8192:
|
domain.CertificateKeyAlgorithmTypeEC512: certcrypto.KeyType("P512"),
|
||||||
return certcrypto.RSA8192
|
}
|
||||||
case domain.CertificateKeyAlgorithmTypeEC256:
|
|
||||||
return certcrypto.EC256
|
if keyType, ok := alogMap[algo]; ok {
|
||||||
case domain.CertificateKeyAlgorithmTypeEC384:
|
return keyType
|
||||||
return certcrypto.EC384
|
|
||||||
case domain.CertificateKeyAlgorithmTypeEC512:
|
|
||||||
return certcrypto.KeyType("P512")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return certcrypto.RSA2048
|
return certcrypto.RSA2048
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
|
|
||||||
type proxyApplicant struct {
|
|
||||||
applicant challenge.Provider
|
|
||||||
options *applicantOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
var limiters sync.Map
|
|
||||||
|
|
||||||
const (
|
|
||||||
limitBurst = 300
|
|
||||||
limitRate float64 = float64(1) / float64(36)
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLimiter(key string) *rate.Limiter {
|
|
||||||
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
|
|
||||||
return limiter.(*rate.Limiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *proxyApplicant) Apply() (*ApplyCertResult, error) {
|
|
||||||
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
|
|
||||||
limiter.Wait(context.Background())
|
|
||||||
return apply(d.applicant, d.options)
|
|
||||||
}
|
|
||||||
|
@ -38,13 +38,31 @@ import (
|
|||||||
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
type applicantProviderOptions struct {
|
||||||
|
Domains []string
|
||||||
|
ContactEmail string
|
||||||
|
Provider domain.ACMEDns01ProviderType
|
||||||
|
ProviderAccessConfig map[string]any
|
||||||
|
ProviderExtendedConfig map[string]any
|
||||||
|
CAProvider domain.CAProviderType
|
||||||
|
CAProviderAccessConfig map[string]any
|
||||||
|
CAProviderExtendedConfig map[string]any
|
||||||
|
KeyAlgorithm string
|
||||||
|
Nameservers []string
|
||||||
|
DnsPropagationTimeout int32
|
||||||
|
DnsTTL int32
|
||||||
|
DisableFollowCNAME bool
|
||||||
|
ReplacedARIAcct string
|
||||||
|
ReplacedARICert string
|
||||||
|
}
|
||||||
|
|
||||||
|
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
|
||||||
/*
|
/*
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch options.Provider {
|
switch options.Provider {
|
||||||
case domain.ApplyDNSProviderTypeACMEHttpReq:
|
case domain.ACMEDns01ProviderTypeACMEHttpReq:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForACMEHttpReq{}
|
access := domain.AccessConfigForACMEHttpReq{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -61,7 +79,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS:
|
case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAliyun{}
|
access := domain.AccessConfigForAliyun{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -77,7 +95,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53:
|
case domain.ACMEDns01ProviderTypeAWS, domain.ACMEDns01ProviderTypeAWSRoute53:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAWS{}
|
access := domain.AccessConfigForAWS{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -95,7 +113,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
|
case domain.ACMEDns01ProviderTypeAzure, domain.ACMEDns01ProviderTypeAzureDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAzure{}
|
access := domain.AccessConfigForAzure{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -113,7 +131,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS:
|
case domain.ACMEDns01ProviderTypeBaiduCloud, domain.ACMEDns01ProviderTypeBaiduCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForBaiduCloud{}
|
access := domain.AccessConfigForBaiduCloud{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -129,7 +147,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeBunny:
|
case domain.ACMEDns01ProviderTypeBunny:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForBunny{}
|
access := domain.AccessConfigForBunny{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -144,7 +162,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeCloudflare:
|
case domain.ACMEDns01ProviderTypeCloudflare:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForCloudflare{}
|
access := domain.AccessConfigForCloudflare{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -160,7 +178,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeClouDNS:
|
case domain.ACMEDns01ProviderTypeClouDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForClouDNS{}
|
access := domain.AccessConfigForClouDNS{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -176,7 +194,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeCMCCCloud:
|
case domain.ACMEDns01ProviderTypeCMCCCloud:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForCMCCCloud{}
|
access := domain.AccessConfigForCMCCCloud{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -192,7 +210,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeDeSEC:
|
case domain.ACMEDns01ProviderTypeDeSEC:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForDeSEC{}
|
access := domain.AccessConfigForDeSEC{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -207,7 +225,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeDNSLA:
|
case domain.ACMEDns01ProviderTypeDNSLA:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForDNSLA{}
|
access := domain.AccessConfigForDNSLA{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -223,7 +241,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeDynv6:
|
case domain.ACMEDns01ProviderTypeDynv6:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForDynv6{}
|
access := domain.AccessConfigForDynv6{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -238,7 +256,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGcore:
|
case domain.ACMEDns01ProviderTypeGcore:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGcore{}
|
access := domain.AccessConfigForGcore{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -253,7 +271,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGname:
|
case domain.ACMEDns01ProviderTypeGname:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGname{}
|
access := domain.AccessConfigForGname{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -269,7 +287,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGoDaddy:
|
case domain.ACMEDns01ProviderTypeGoDaddy:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGoDaddy{}
|
access := domain.AccessConfigForGoDaddy{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -285,7 +303,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS:
|
case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForHuaweiCloud{}
|
access := domain.AccessConfigForHuaweiCloud{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -302,7 +320,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
|
case domain.ACMEDns01ProviderTypeJDCloud, domain.ACMEDns01ProviderTypeJDCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForJDCloud{}
|
access := domain.AccessConfigForJDCloud{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -319,7 +337,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNamecheap:
|
case domain.ACMEDns01ProviderTypeNamecheap:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNamecheap{}
|
access := domain.AccessConfigForNamecheap{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -335,7 +353,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNameDotCom:
|
case domain.ACMEDns01ProviderTypeNameDotCom:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNameDotCom{}
|
access := domain.AccessConfigForNameDotCom{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -351,7 +369,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNameSilo:
|
case domain.ACMEDns01ProviderTypeNameSilo:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNameSilo{}
|
access := domain.AccessConfigForNameSilo{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -366,7 +384,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNS1:
|
case domain.ACMEDns01ProviderTypeNS1:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNS1{}
|
access := domain.AccessConfigForNS1{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -381,7 +399,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypePorkbun:
|
case domain.ACMEDns01ProviderTypePorkbun:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForPorkbun{}
|
access := domain.AccessConfigForPorkbun{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -397,7 +415,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypePowerDNS:
|
case domain.ACMEDns01ProviderTypePowerDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForPowerDNS{}
|
access := domain.AccessConfigForPowerDNS{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -413,7 +431,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeRainYun:
|
case domain.ACMEDns01ProviderTypeRainYun:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForRainYun{}
|
access := domain.AccessConfigForRainYun{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -428,7 +446,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS, domain.ApplyDNSProviderTypeTencentCloudEO:
|
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS, domain.ACMEDns01ProviderTypeTencentCloudEO:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForTencentCloud{}
|
access := domain.AccessConfigForTencentCloud{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -436,7 +454,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch options.Provider {
|
switch options.Provider {
|
||||||
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
|
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS:
|
||||||
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
|
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
|
||||||
SecretId: access.SecretId,
|
SecretId: access.SecretId,
|
||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
@ -445,7 +463,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeTencentCloudEO:
|
case domain.ACMEDns01ProviderTypeTencentCloudEO:
|
||||||
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
|
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
|
||||||
SecretId: access.SecretId,
|
SecretId: access.SecretId,
|
||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
@ -460,7 +478,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeVercel:
|
case domain.ACMEDns01ProviderTypeVercel:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForVercel{}
|
access := domain.AccessConfigForVercel{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -476,7 +494,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS:
|
case domain.ACMEDns01ProviderTypeVolcEngine, domain.ACMEDns01ProviderTypeVolcEngineDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForVolcEngine{}
|
access := domain.AccessConfigForVolcEngine{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
@ -492,7 +510,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeWestcn:
|
case domain.ACMEDns01ProviderTypeWestcn:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForWestcn{}
|
access := domain.AccessConfigForWestcn{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
@ -11,31 +11,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Deployer interface {
|
type Deployer interface {
|
||||||
SetLogger(*slog.Logger)
|
|
||||||
|
|
||||||
Deploy(ctx context.Context) error
|
Deploy(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type deployerOptions struct {
|
type DeployerWithWorkflowNodeConfig struct {
|
||||||
Provider domain.DeployProviderType
|
Node *domain.WorkflowNode
|
||||||
ProviderAccessConfig map[string]any
|
Logger *slog.Logger
|
||||||
ProviderDeployConfig map[string]any
|
CertificatePEM string
|
||||||
|
PrivateKeyPEM string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
|
func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error) {
|
||||||
Certificate string
|
if config.Node == nil {
|
||||||
PrivateKey string
|
return nil, fmt.Errorf("node is nil")
|
||||||
},
|
}
|
||||||
) (Deployer, error) {
|
if config.Node.Type != domain.WorkflowNodeTypeDeploy {
|
||||||
if node.Type != domain.WorkflowNodeTypeDeploy {
|
|
||||||
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeDeploy))
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeDeploy))
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig := node.GetConfigForDeploy()
|
nodeConfig := config.Node.GetConfigForDeploy()
|
||||||
options := &deployerOptions{
|
options := &deployerProviderOptions{
|
||||||
Provider: domain.DeployProviderType(nodeConfig.Provider),
|
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
|
||||||
ProviderAccessConfig: make(map[string]any),
|
ProviderAccessConfig: make(map[string]any),
|
||||||
ProviderDeployConfig: nodeConfig.ProviderConfig,
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
@ -48,34 +46,27 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deployer, err := createDeployer(options)
|
deployerProvider, err := createDeployerProvider(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyDeployer{
|
return &deployerImpl{
|
||||||
deployer: deployer,
|
provider: deployerProvider.WithLogger(config.Logger),
|
||||||
deployCertificate: certdata.Certificate,
|
certPEM: config.CertificatePEM,
|
||||||
deployPrivateKey: certdata.PrivateKey,
|
privkeyPEM: config.PrivateKeyPEM,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
|
type deployerImpl struct {
|
||||||
type proxyDeployer struct {
|
provider deployer.Deployer
|
||||||
deployer deployer.Deployer
|
certPEM string
|
||||||
deployCertificate string
|
privkeyPEM string
|
||||||
deployPrivateKey string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *proxyDeployer) SetLogger(logger *slog.Logger) {
|
var _ Deployer = (*deployerImpl)(nil)
|
||||||
if logger == nil {
|
|
||||||
panic("logger is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.deployer.WithLogger(logger)
|
func (d *deployerImpl) Deploy(ctx context.Context) error {
|
||||||
}
|
_, err := d.provider.Deploy(ctx, d.certPEM, d.privkeyPEM)
|
||||||
|
|
||||||
func (d *proxyDeployer) Deploy(ctx context.Context) error {
|
|
||||||
_, err := d.deployer.Deploy(ctx, d.deployCertificate, d.deployPrivateKey)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@ type Access struct {
|
|||||||
Name string `json:"name" db:"name"`
|
Name string `json:"name" db:"name"`
|
||||||
Provider string `json:"provider" db:"provider"`
|
Provider string `json:"provider" db:"provider"`
|
||||||
Config map[string]any `json:"config" db:"config"`
|
Config map[string]any `json:"config" db:"config"`
|
||||||
|
Reserve string `json:"reserve,omitempty" db:"reserve"`
|
||||||
DeletedAt *time.Time `json:"deleted" db:"deleted"`
|
DeletedAt *time.Time `json:"deleted" db:"deleted"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +98,11 @@ type AccessConfigForDeSEC struct {
|
|||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForDingTalkBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForDNSLA struct {
|
type AccessConfigForDNSLA struct {
|
||||||
ApiId string `json:"apiId"`
|
ApiId string `json:"apiId"`
|
||||||
ApiSecret string `json:"apiSecret"`
|
ApiSecret string `json:"apiSecret"`
|
||||||
@ -116,6 +122,16 @@ type AccessConfigForEdgio struct {
|
|||||||
ClientSecret string `json:"clientSecret"`
|
ClientSecret string `json:"clientSecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForEmail struct {
|
||||||
|
SmtpHost string `json:"smtpHost"`
|
||||||
|
SmtpPort int32 `json:"smtpPort"`
|
||||||
|
SmtpTls bool `json:"smtpTls"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
DefaultSenderAddress string `json:"defaultSenderAddress,omitempty"`
|
||||||
|
DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForGcore struct {
|
type AccessConfigForGcore struct {
|
||||||
ApiToken string `json:"apiToken"`
|
ApiToken string `json:"apiToken"`
|
||||||
}
|
}
|
||||||
@ -149,6 +165,17 @@ type AccessConfigForKubernetes struct {
|
|||||||
KubeConfig string `json:"kubeConfig,omitempty"`
|
KubeConfig string `json:"kubeConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForLarkBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForMattermost struct {
|
||||||
|
ServerUrl string `json:"serverUrl"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
DefaultChannelId string `json:"defaultChannelId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForNamecheap struct {
|
type AccessConfigForNamecheap struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
@ -206,6 +233,11 @@ type AccessConfigForSSLCom struct {
|
|||||||
EabHmacKey string `json:"eabHmacKey"`
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForTelegram struct {
|
||||||
|
BotToken string `json:"botToken"`
|
||||||
|
DefaultChatId int64 `json:"defaultChatId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForTencentCloud struct {
|
type AccessConfigForTencentCloud struct {
|
||||||
SecretId string `json:"secretId"`
|
SecretId string `json:"secretId"`
|
||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
@ -239,8 +271,16 @@ type AccessConfigForWangsu struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForWebhook struct {
|
type AccessConfigForWebhook struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
Method string `json:"method,omitempty"`
|
||||||
|
HeadersString string `json:"headers,omitempty"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
DefaultDataForDeployment string `json:"defaultDataForDeployment,omitempty"`
|
||||||
|
DefaultDataForNotification string `json:"defaultDataForNotification,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForWeComBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForWestcn struct {
|
type AccessConfigForWestcn struct {
|
||||||
|
@ -8,6 +8,7 @@ type NotifyChannelType string
|
|||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
const (
|
const (
|
||||||
NotifyChannelTypeBark = NotifyChannelType("bark")
|
NotifyChannelTypeBark = NotifyChannelType("bark")
|
||||||
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")
|
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
||||||
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
||||||
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
||||||
|
AccessProviderTypeBunny = AccessProviderType("bunny")
|
||||||
AccessProviderTypeBuypass = AccessProviderType("buypass")
|
AccessProviderTypeBuypass = AccessProviderType("buypass")
|
||||||
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
|
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
|
||||||
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
|
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
|
||||||
@ -28,10 +29,12 @@ const (
|
|||||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
|
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
|
||||||
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
|
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
|
||||||
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
||||||
|
AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot")
|
||||||
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
||||||
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
||||||
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
||||||
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
||||||
|
AccessProviderTypeEmail = AccessProviderType("email")
|
||||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||||
AccessProviderTypeGname = AccessProviderType("gname")
|
AccessProviderTypeGname = AccessProviderType("gname")
|
||||||
AccessProviderTypeGcore = AccessProviderType("gcore")
|
AccessProviderTypeGcore = AccessProviderType("gcore")
|
||||||
@ -41,9 +44,11 @@ const (
|
|||||||
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
||||||
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
||||||
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
||||||
|
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
||||||
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
||||||
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
|
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
|
||||||
AccessProviderTypeLocal = AccessProviderType("local")
|
AccessProviderTypeLocal = AccessProviderType("local")
|
||||||
|
AccessProviderTypeMattermost = AccessProviderType("mattermost")
|
||||||
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
||||||
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
|
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
|
||||||
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
|
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
|
||||||
@ -56,6 +61,7 @@ const (
|
|||||||
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
||||||
AccessProviderTypeSSH = AccessProviderType("ssh")
|
AccessProviderTypeSSH = AccessProviderType("ssh")
|
||||||
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
|
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
|
||||||
|
AccessProviderTypeTelegram = AccessProviderType("telegram")
|
||||||
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
||||||
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
||||||
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
||||||
@ -63,78 +69,79 @@ const (
|
|||||||
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
|
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
|
||||||
AccessProviderTypeWangsu = AccessProviderType("wangsu")
|
AccessProviderTypeWangsu = AccessProviderType("wangsu")
|
||||||
AccessProviderTypeWebhook = AccessProviderType("webhook")
|
AccessProviderTypeWebhook = AccessProviderType("webhook")
|
||||||
|
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
|
||||||
AccessProviderTypeWestcn = AccessProviderType("westcn")
|
AccessProviderTypeWestcn = AccessProviderType("westcn")
|
||||||
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
|
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyCAProviderType string
|
type CAProviderType string
|
||||||
|
|
||||||
/*
|
/*
|
||||||
申请证书 CA 提供商常量值。
|
证书颁发机构提供商常量值。
|
||||||
始终等于授权提供商类型。
|
|
||||||
|
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
|
||||||
*/
|
|
||||||
const (
|
|
||||||
ApplyCAProviderTypeBuypass = ApplyCAProviderType(string(AccessProviderTypeBuypass))
|
|
||||||
ApplyCAProviderTypeGoogleTrustServices = ApplyCAProviderType(string(AccessProviderTypeGoogleTrustServices))
|
|
||||||
ApplyCAProviderTypeLetsEncrypt = ApplyCAProviderType(string(AccessProviderTypeLetsEncrypt))
|
|
||||||
ApplyCAProviderTypeLetsEncryptStaging = ApplyCAProviderType(string(AccessProviderTypeLetsEncryptStaging))
|
|
||||||
ApplyCAProviderTypeSSLCom = ApplyCAProviderType(string(AccessProviderTypeSSLCOM))
|
|
||||||
ApplyCAProviderTypeZeroSSL = ApplyCAProviderType(string(AccessProviderTypeZeroSSL))
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApplyDNSProviderType string
|
|
||||||
|
|
||||||
/*
|
|
||||||
申请证书 DNS 提供商常量值。
|
|
||||||
短横线前的部分始终等于授权提供商类型。
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
ApplyDNSProviderTypeACMEHttpReq = ApplyDNSProviderType("acmehttpreq")
|
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
|
||||||
ApplyDNSProviderTypeAliyun = ApplyDNSProviderType("aliyun") // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS]
|
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
|
||||||
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns")
|
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
|
||||||
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
|
CAProviderTypeLetsEncryptStaging = CAProviderType(AccessProviderTypeLetsEncryptStaging)
|
||||||
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
|
CAProviderTypeSSLCom = CAProviderType(AccessProviderTypeSSLCOM)
|
||||||
ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
|
CAProviderTypeZeroSSL = CAProviderType(AccessProviderTypeZeroSSL)
|
||||||
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
|
|
||||||
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
|
|
||||||
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns")
|
|
||||||
ApplyDNSProviderTypeBunny = ApplyDNSProviderType("bunny")
|
|
||||||
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
|
|
||||||
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
|
|
||||||
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud")
|
|
||||||
ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType("desec")
|
|
||||||
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla")
|
|
||||||
ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType("dynv6")
|
|
||||||
ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore")
|
|
||||||
ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname")
|
|
||||||
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
|
|
||||||
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
|
|
||||||
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
|
|
||||||
ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS]
|
|
||||||
ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns")
|
|
||||||
ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType("namecheap")
|
|
||||||
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
|
|
||||||
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
|
|
||||||
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
|
|
||||||
ApplyDNSProviderTypePorkbun = ApplyDNSProviderType("porkbun")
|
|
||||||
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns")
|
|
||||||
ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun")
|
|
||||||
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
|
|
||||||
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns")
|
|
||||||
ApplyDNSProviderTypeTencentCloudEO = ApplyDNSProviderType("tencentcloud-eo")
|
|
||||||
ApplyDNSProviderTypeVercel = ApplyDNSProviderType("vercel")
|
|
||||||
ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS]
|
|
||||||
ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns")
|
|
||||||
ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployProviderType string
|
type ACMEDns01ProviderType string
|
||||||
|
|
||||||
|
/*
|
||||||
|
ACME DNS-01 提供商常量值。
|
||||||
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
|
||||||
|
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
|
||||||
|
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
|
||||||
|
ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53]
|
||||||
|
ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53")
|
||||||
|
ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure]
|
||||||
|
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
|
||||||
|
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
|
||||||
|
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
|
||||||
|
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
|
||||||
|
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
||||||
|
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
||||||
|
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
|
||||||
|
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||||
|
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||||
|
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
|
||||||
|
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
|
||||||
|
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
|
||||||
|
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
|
||||||
|
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
|
||||||
|
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
|
||||||
|
ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS]
|
||||||
|
ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns")
|
||||||
|
ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap)
|
||||||
|
ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom)
|
||||||
|
ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo)
|
||||||
|
ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1)
|
||||||
|
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
|
||||||
|
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
|
||||||
|
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||||
|
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||||
|
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
||||||
|
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||||
|
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||||
|
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||||
|
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||||
|
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeploymentProviderType string
|
||||||
|
|
||||||
/*
|
/*
|
||||||
部署证书主机提供商常量值。
|
部署证书主机提供商常量值。
|
||||||
@ -144,78 +151,97 @@ type DeployProviderType string
|
|||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
DeployProviderType1PanelConsole = DeployProviderType("1panel-console")
|
DeploymentProviderType1PanelConsole = DeploymentProviderType(AccessProviderType1Panel + "-console")
|
||||||
DeployProviderType1PanelSite = DeployProviderType("1panel-site")
|
DeploymentProviderType1PanelSite = DeploymentProviderType(AccessProviderType1Panel + "-site")
|
||||||
DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb")
|
DeploymentProviderTypeAliyunALB = DeploymentProviderType(AccessProviderTypeAliyun + "-alb")
|
||||||
DeployProviderTypeAliyunAPIGW = DeployProviderType("aliyun-apigw")
|
DeploymentProviderTypeAliyunAPIGW = DeploymentProviderType(AccessProviderTypeAliyun + "-apigw")
|
||||||
DeployProviderTypeAliyunCAS = DeployProviderType("aliyun-cas")
|
DeploymentProviderTypeAliyunCAS = DeploymentProviderType(AccessProviderTypeAliyun + "-cas")
|
||||||
DeployProviderTypeAliyunCASDeploy = DeployProviderType("aliyun-casdeploy")
|
DeploymentProviderTypeAliyunCASDeploy = DeploymentProviderType(AccessProviderTypeAliyun + "-casdeploy")
|
||||||
DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn")
|
DeploymentProviderTypeAliyunCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-cdn")
|
||||||
DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb")
|
DeploymentProviderTypeAliyunCLB = DeploymentProviderType(AccessProviderTypeAliyun + "-clb")
|
||||||
DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn")
|
DeploymentProviderTypeAliyunDCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-dcdn")
|
||||||
DeployProviderTypeAliyunESA = DeployProviderType("aliyun-esa")
|
DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa")
|
||||||
DeployProviderTypeAliyunFC = DeployProviderType("aliyun-fc")
|
DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc")
|
||||||
DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live")
|
DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live")
|
||||||
DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb")
|
DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb")
|
||||||
DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss")
|
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
|
||||||
DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod")
|
DeploymentProviderTypeAliyunVOD = DeploymentProviderType(AccessProviderTypeAliyun + "-vod")
|
||||||
DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf")
|
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
|
||||||
DeployProviderTypeAWSACM = DeployProviderType("aws-acm")
|
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
|
||||||
DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront")
|
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
|
||||||
DeployProviderTypeAzureKeyVault = DeployProviderType("azure-keyvault")
|
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
|
||||||
DeployProviderTypeBaiduCloudAppBLB = DeployProviderType("baiducloud-appblb")
|
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
|
||||||
DeployProviderTypeBaiduCloudBLB = DeployProviderType("baiducloud-blb")
|
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
|
||||||
DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn")
|
DeploymentProviderTypeBaiduCloudCDN = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cdn")
|
||||||
DeployProviderTypeBaiduCloudCert = DeployProviderType("baiducloud-cert")
|
DeploymentProviderTypeBaiduCloudCert = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cert")
|
||||||
DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn")
|
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
|
||||||
DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console")
|
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
|
||||||
DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site")
|
DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site")
|
||||||
DeployProviderTypeBunnyCDN = DeployProviderType("bunny-cdn")
|
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
|
||||||
DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn")
|
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
|
||||||
DeployProviderTypeCacheFly = DeployProviderType("cachefly")
|
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
|
||||||
DeployProviderTypeCdnfly = DeployProviderType("cdnfly")
|
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
|
||||||
DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn")
|
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
|
||||||
DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications")
|
DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications")
|
||||||
DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn")
|
DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn")
|
||||||
DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn")
|
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
|
||||||
DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb")
|
DeploymentProviderTypeHuaweiCloudELB = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-elb")
|
||||||
DeployProviderTypeHuaweiCloudSCM = DeployProviderType("huaweicloud-scm")
|
DeploymentProviderTypeHuaweiCloudSCM = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-scm")
|
||||||
DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf")
|
DeploymentProviderTypeHuaweiCloudWAF = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-waf")
|
||||||
DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb")
|
DeploymentProviderTypeJDCloudALB = DeploymentProviderType(AccessProviderTypeJDCloud + "-alb")
|
||||||
DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn")
|
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")
|
||||||
DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live")
|
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
||||||
DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod")
|
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
||||||
DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret")
|
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
||||||
DeployProviderTypeLocal = DeployProviderType("local")
|
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
||||||
DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn")
|
DeploymentProviderTypeQiniuCDN = DeploymentProviderType(AccessProviderTypeQiniu + "-cdn")
|
||||||
DeployProviderTypeQiniuKodo = DeployProviderType("qiniu-kodo")
|
DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo")
|
||||||
DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili")
|
DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili")
|
||||||
DeployProviderTypeRainYunRCDN = DeployProviderType("rainyun-rcdn")
|
DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn")
|
||||||
DeployProviderTypeSafeLine = DeployProviderType("safeline")
|
DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine)
|
||||||
DeployProviderTypeSSH = DeployProviderType("ssh")
|
DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH)
|
||||||
DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn")
|
DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn")
|
||||||
DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb")
|
DeploymentProviderTypeTencentCloudCLB = DeploymentProviderType(AccessProviderTypeTencentCloud + "-clb")
|
||||||
DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos")
|
DeploymentProviderTypeTencentCloudCOS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cos")
|
||||||
DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css")
|
DeploymentProviderTypeTencentCloudCSS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-css")
|
||||||
DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn")
|
DeploymentProviderTypeTencentCloudECDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ecdn")
|
||||||
DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo")
|
DeploymentProviderTypeTencentCloudEO = DeploymentProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||||
DeployProviderTypeTencentCloudSCF = DeployProviderType("tencentcloud-scf")
|
DeploymentProviderTypeTencentCloudSCF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-scf")
|
||||||
DeployProviderTypeTencentCloudSSL = DeployProviderType("tencentcloud-ssl")
|
DeploymentProviderTypeTencentCloudSSL = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssl")
|
||||||
DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy")
|
DeploymentProviderTypeTencentCloudSSLDeploy = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssldeploy")
|
||||||
DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod")
|
DeploymentProviderTypeTencentCloudVOD = DeploymentProviderType(AccessProviderTypeTencentCloud + "-vod")
|
||||||
DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf")
|
DeploymentProviderTypeTencentCloudWAF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-waf")
|
||||||
DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn")
|
DeploymentProviderTypeUCloudUCDN = DeploymentProviderType(AccessProviderTypeUCloud + "-ucdn")
|
||||||
DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3")
|
DeploymentProviderTypeUCloudUS3 = DeploymentProviderType(AccessProviderTypeUCloud + "-us3")
|
||||||
DeployProviderTypeUpyunCDN = DeployProviderType("upyun-cdn")
|
DeploymentProviderTypeUpyunCDN = DeploymentProviderType(AccessProviderTypeUpyun + "-cdn")
|
||||||
DeployProviderTypeUpyunFile = DeployProviderType("upyun-file")
|
DeploymentProviderTypeUpyunFile = DeploymentProviderType(AccessProviderTypeUpyun + "-file")
|
||||||
DeployProviderTypeVolcEngineALB = DeployProviderType("volcengine-alb")
|
DeploymentProviderTypeVolcEngineALB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-alb")
|
||||||
DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn")
|
DeploymentProviderTypeVolcEngineCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-cdn")
|
||||||
DeployProviderTypeVolcEngineCertCenter = DeployProviderType("volcengine-certcenter")
|
DeploymentProviderTypeVolcEngineCertCenter = DeploymentProviderType(AccessProviderTypeVolcEngine + "-certcenter")
|
||||||
DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb")
|
DeploymentProviderTypeVolcEngineCLB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-clb")
|
||||||
DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn")
|
DeploymentProviderTypeVolcEngineDCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-dcdn")
|
||||||
DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex")
|
DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex")
|
||||||
DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live")
|
DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live")
|
||||||
DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos")
|
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
|
||||||
DeployProviderTypeWangsuCDNPro = DeployProviderType("wangsu-cdnpro")
|
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
|
||||||
DeployProviderTypeWebhook = DeployProviderType("webhook")
|
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationProviderType string
|
||||||
|
|
||||||
|
/*
|
||||||
|
消息通知提供商常量值。
|
||||||
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
NotificationProviderTypeDingTalkBot = NotificationProviderType(AccessProviderTypeDingTalkBot)
|
||||||
|
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
|
||||||
|
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
|
||||||
|
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
|
||||||
|
NotificationProviderTypeTelegram = NotificationProviderType(AccessProviderTypeTelegram)
|
||||||
|
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
|
||||||
|
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
|
||||||
)
|
)
|
||||||
|
@ -13,6 +13,7 @@ type Settings struct {
|
|||||||
Content string `json:"content" db:"content"`
|
Content string `json:"content" db:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyTemplatesSettingsContent struct {
|
type NotifyTemplatesSettingsContent struct {
|
||||||
NotifyTemplates []struct {
|
NotifyTemplates []struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
@ -20,8 +21,10 @@ type NotifyTemplatesSettingsContent struct {
|
|||||||
} `json:"notifyTemplates"`
|
} `json:"notifyTemplates"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyChannelsSettingsContent map[string]map[string]any
|
type NotifyChannelsSettingsContent map[string]map[string]any
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) {
|
func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) {
|
||||||
conf := &NotifyChannelsSettingsContent{}
|
conf := &NotifyChannelsSettingsContent{}
|
||||||
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {
|
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {
|
||||||
|
@ -71,7 +71,7 @@ type WorkflowNodeConfigForApply struct {
|
|||||||
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值将使用全局配置)
|
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值将使用全局配置)
|
||||||
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
||||||
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
||||||
KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法
|
KeyAlgorithm string `json:"keyAlgorithm"` // 证书算法
|
||||||
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
||||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播超时时间(零值取决于提供商的默认值)
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播超时时间(零值取决于提供商的默认值)
|
||||||
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS TTL(零值取决于提供商的默认值)
|
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS TTL(零值取决于提供商的默认值)
|
||||||
@ -95,9 +95,12 @@ type WorkflowNodeConfigForDeploy struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeConfigForNotify struct {
|
type WorkflowNodeConfigForNotify struct {
|
||||||
Channel string `json:"channel"` // 通知渠道
|
Channel string `json:"channel,omitempty"` // Deprecated: v0.4.x 将废弃
|
||||||
Subject string `json:"subject"` // 通知主题
|
Provider string `json:"provider"` // 通知提供商
|
||||||
Message string `json:"message"` // 通知内容
|
ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID
|
||||||
|
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
|
||||||
|
Subject string `json:"subject"` // 通知主题
|
||||||
|
Message string `json:"message"` // 通知内容
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
||||||
@ -111,10 +114,10 @@ func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
|||||||
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
|
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
|
||||||
Provider: maputil.GetString(n.Config, "provider"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
ProviderConfig: maputil.GetAnyMap(n.Config, "providerConfig"),
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
CAProvider: maputil.GetString(n.Config, "caProvider"),
|
CAProvider: maputil.GetString(n.Config, "caProvider"),
|
||||||
CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"),
|
CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"),
|
||||||
CAProviderConfig: maputil.GetAnyMap(n.Config, "caProviderConfig"),
|
CAProviderConfig: maputil.GetKVMapAny(n.Config, "caProviderConfig"),
|
||||||
KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"),
|
KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"),
|
||||||
Nameservers: maputil.GetString(n.Config, "nameservers"),
|
Nameservers: maputil.GetString(n.Config, "nameservers"),
|
||||||
DnsPropagationTimeout: maputil.GetInt32(n.Config, "dnsPropagationTimeout"),
|
DnsPropagationTimeout: maputil.GetInt32(n.Config, "dnsPropagationTimeout"),
|
||||||
@ -138,16 +141,19 @@ func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
|
|||||||
Certificate: maputil.GetString(n.Config, "certificate"),
|
Certificate: maputil.GetString(n.Config, "certificate"),
|
||||||
Provider: maputil.GetString(n.Config, "provider"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
ProviderConfig: maputil.GetAnyMap(n.Config, "providerConfig"),
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
|
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
|
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
|
||||||
return WorkflowNodeConfigForNotify{
|
return WorkflowNodeConfigForNotify{
|
||||||
Channel: maputil.GetString(n.Config, "channel"),
|
Channel: maputil.GetString(n.Config, "channel"),
|
||||||
Subject: maputil.GetString(n.Config, "subject"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
Message: maputil.GetString(n.Config, "message"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
|
Subject: maputil.GetString(n.Config, "subject"),
|
||||||
|
Message: maputil.GetString(n.Config, "message"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
internal/notify/notifier.go
Normal file
72
internal/notify/notifier.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Notifier interface {
|
||||||
|
Notify(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotifierWithWorkflowNodeConfig struct {
|
||||||
|
Node *domain.WorkflowNode
|
||||||
|
Logger *slog.Logger
|
||||||
|
Subject string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error) {
|
||||||
|
if config.Node == nil {
|
||||||
|
return nil, fmt.Errorf("node is nil")
|
||||||
|
}
|
||||||
|
if config.Node.Type != domain.WorkflowNodeTypeNotify {
|
||||||
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeNotify))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeConfig := config.Node.GetConfigForNotify()
|
||||||
|
options := ¬ifierProviderOptions{
|
||||||
|
Provider: domain.NotificationProviderType(nodeConfig.Provider),
|
||||||
|
ProviderAccessConfig: make(map[string]any),
|
||||||
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
accessRepo := repository.NewAccessRepository()
|
||||||
|
if nodeConfig.ProviderAccessId != "" {
|
||||||
|
access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
||||||
|
} else {
|
||||||
|
options.ProviderAccessConfig = access.Config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifierProvider, err := createNotifierProvider(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ¬ifierImpl{
|
||||||
|
provider: notifierProvider.WithLogger(config.Logger),
|
||||||
|
subject: config.Subject,
|
||||||
|
message: config.Message,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type notifierImpl struct {
|
||||||
|
provider notifier.Notifier
|
||||||
|
subject string
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Notifier = (*notifierImpl)(nil)
|
||||||
|
|
||||||
|
func (n *notifierImpl) Notify(ctx context.Context) error {
|
||||||
|
_, err := n.provider.Notify(ctx, n.subject, n.message)
|
||||||
|
return err
|
||||||
|
}
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func SendToAllChannels(subject, message string) error {
|
func SendToAllChannels(subject, message string) error {
|
||||||
notifiers, err := getEnabledNotifiers()
|
notifiers, err := getEnabledNotifiers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,8 +39,9 @@ func SendToAllChannels(subject, message string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func SendToChannel(subject, message string, channel string, channelConfig map[string]any) error {
|
func SendToChannel(subject, message string, channel string, channelConfig map[string]any) error {
|
||||||
notifier, err := createNotifier(domain.NotifyChannelType(channel), channelConfig)
|
notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(channel), channelConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -48,6 +50,7 @@ func SendToChannel(subject, message string, channel string, channelConfig map[st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
||||||
settingsRepo := repository.NewSettingsRepository()
|
settingsRepo := repository.NewSettingsRepository()
|
||||||
settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels")
|
settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels")
|
||||||
@ -66,7 +69,7 @@ func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier, err := createNotifier(domain.NotifyChannelType(k), v)
|
notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(k), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -2,105 +2,152 @@ package notify
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
|
||||||
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
||||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
|
||||||
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
||||||
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
|
||||||
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
|
||||||
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
|
||||||
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
||||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
||||||
|
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
|
||||||
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) {
|
type notifierProviderOptions struct {
|
||||||
|
Provider domain.NotificationProviderType
|
||||||
|
ProviderAccessConfig map[string]any
|
||||||
|
ProviderExtendedConfig map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) {
|
||||||
/*
|
/*
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch channel {
|
switch options.Provider {
|
||||||
case domain.NotifyChannelTypeBark:
|
case domain.NotificationProviderTypeDingTalkBot:
|
||||||
return pBark.NewNotifier(&pBark.NotifierConfig{
|
{
|
||||||
DeviceKey: maputil.GetString(channelConfig, "deviceKey"),
|
access := domain.AccessConfigForDingTalkBot{}
|
||||||
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
})
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeDingTalk:
|
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
||||||
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
WebhookUrl: access.WebhookUrl,
|
||||||
AccessToken: maputil.GetString(channelConfig, "accessToken"),
|
Secret: access.Secret,
|
||||||
Secret: maputil.GetString(channelConfig, "secret"),
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeEmail:
|
case domain.NotificationProviderTypeEmail:
|
||||||
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
{
|
||||||
SmtpHost: maputil.GetString(channelConfig, "smtpHost"),
|
access := domain.AccessConfigForEmail{}
|
||||||
SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
SmtpTLS: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true),
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")),
|
}
|
||||||
Password: maputil.GetString(channelConfig, "password"),
|
|
||||||
SenderAddress: maputil.GetString(channelConfig, "senderAddress"),
|
|
||||||
ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"),
|
|
||||||
})
|
|
||||||
|
|
||||||
case domain.NotifyChannelTypeGotify:
|
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
||||||
return pGotify.NewNotifier(&pGotify.NotifierConfig{
|
SmtpHost: access.SmtpHost,
|
||||||
Url: maputil.GetString(channelConfig, "url"),
|
SmtpPort: access.SmtpPort,
|
||||||
Token: maputil.GetString(channelConfig, "token"),
|
SmtpTls: access.SmtpTls,
|
||||||
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
|
Username: access.Username,
|
||||||
})
|
Password: access.Password,
|
||||||
|
SenderAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "senderAddress", access.DefaultSenderAddress),
|
||||||
|
ReceiverAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "receiverAddress", access.DefaultReceiverAddress),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeLark:
|
case domain.NotificationProviderTypeLarkBot:
|
||||||
return pLark.NewNotifier(&pLark.NotifierConfig{
|
{
|
||||||
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
access := domain.AccessConfigForLarkBot{}
|
||||||
})
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeMattermost:
|
return pLark.NewNotifier(&pLark.NotifierConfig{
|
||||||
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
|
WebhookUrl: access.WebhookUrl,
|
||||||
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
})
|
||||||
ChannelId: maputil.GetString(channelConfig, "channelId"),
|
}
|
||||||
Username: maputil.GetString(channelConfig, "username"),
|
|
||||||
Password: maputil.GetString(channelConfig, "password"),
|
|
||||||
})
|
|
||||||
case domain.NotifyChannelTypePushover:
|
|
||||||
return pPushover.NewNotifier(&pPushover.NotifierConfig{
|
|
||||||
Token: maputil.GetString(channelConfig, "token"),
|
|
||||||
User: maputil.GetString(channelConfig, "user"),
|
|
||||||
})
|
|
||||||
|
|
||||||
case domain.NotifyChannelTypePushPlus:
|
case domain.NotificationProviderTypeMattermost:
|
||||||
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
|
{
|
||||||
Token: maputil.GetString(channelConfig, "token"),
|
access := domain.AccessConfigForMattermost{}
|
||||||
})
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeServerChan:
|
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
|
||||||
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
ServerUrl: access.ServerUrl,
|
||||||
Url: maputil.GetString(channelConfig, "url"),
|
Username: access.Username,
|
||||||
})
|
Password: access.Password,
|
||||||
|
ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeTelegram:
|
case domain.NotificationProviderTypeTelegram:
|
||||||
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
{
|
||||||
ApiToken: maputil.GetString(channelConfig, "apiToken"),
|
access := domain.AccessConfigForTelegram{}
|
||||||
ChatId: maputil.GetInt64(channelConfig, "chatId"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
})
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeWebhook:
|
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
||||||
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
BotToken: access.BotToken,
|
||||||
Url: maputil.GetString(channelConfig, "url"),
|
ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId),
|
||||||
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"),
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeWeCom:
|
case domain.NotificationProviderTypeWebhook:
|
||||||
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
{
|
||||||
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
access := domain.AccessConfigForWebhook{}
|
||||||
})
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedHeaders := make(map[string]string)
|
||||||
|
if defaultHeadersString := access.HeadersString; defaultHeadersString != "" {
|
||||||
|
h, err := httputil.ParseHeaders(defaultHeadersString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
||||||
|
}
|
||||||
|
for key := range h {
|
||||||
|
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" {
|
||||||
|
h, err := httputil.ParseHeaders(extendedHeadersString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
||||||
|
}
|
||||||
|
for key := range h {
|
||||||
|
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
||||||
|
WebhookUrl: access.Url,
|
||||||
|
WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification),
|
||||||
|
Method: access.Method,
|
||||||
|
Headers: mergedHeaders,
|
||||||
|
AllowInsecureConnections: access.AllowInsecureConnections,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.NotificationProviderTypeWeComBot:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForWeComBot{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
||||||
|
WebhookUrl: access.WebhookUrl,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unsupported notifier channel '%s'", channelConfig)
|
return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider)
|
||||||
}
|
}
|
||||||
|
108
internal/notify/providers_deprecated.go
Normal file
108
internal/notify/providers_deprecated.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
|
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
||||||
|
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
||||||
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
|
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
||||||
|
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
||||||
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
|
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
||||||
|
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
||||||
|
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
||||||
|
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
||||||
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
|
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
||||||
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
|
func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) {
|
||||||
|
/*
|
||||||
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
|
*/
|
||||||
|
switch channel {
|
||||||
|
case domain.NotifyChannelTypeBark:
|
||||||
|
return pBark.NewNotifier(&pBark.NotifierConfig{
|
||||||
|
DeviceKey: maputil.GetString(channelConfig, "deviceKey"),
|
||||||
|
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeDingTalk:
|
||||||
|
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
||||||
|
WebhookUrl: "https://oapi.dingtalk.com/robot/send?access_token=" + maputil.GetString(channelConfig, "accessToken"),
|
||||||
|
Secret: maputil.GetString(channelConfig, "secret"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeEmail:
|
||||||
|
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
||||||
|
SmtpHost: maputil.GetString(channelConfig, "smtpHost"),
|
||||||
|
SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"),
|
||||||
|
SmtpTls: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true),
|
||||||
|
Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")),
|
||||||
|
Password: maputil.GetString(channelConfig, "password"),
|
||||||
|
SenderAddress: maputil.GetString(channelConfig, "senderAddress"),
|
||||||
|
ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeGotify:
|
||||||
|
return pGotify.NewNotifier(&pGotify.NotifierConfig{
|
||||||
|
Url: maputil.GetString(channelConfig, "url"),
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeLark:
|
||||||
|
return pLark.NewNotifier(&pLark.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeMattermost:
|
||||||
|
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
|
||||||
|
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
||||||
|
ChannelId: maputil.GetString(channelConfig, "channelId"),
|
||||||
|
Username: maputil.GetString(channelConfig, "username"),
|
||||||
|
Password: maputil.GetString(channelConfig, "password"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypePushover:
|
||||||
|
return pPushover.NewNotifier(&pPushover.NotifierConfig{
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
User: maputil.GetString(channelConfig, "user"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypePushPlus:
|
||||||
|
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeServerChan:
|
||||||
|
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
||||||
|
Url: maputil.GetString(channelConfig, "url"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeTelegram:
|
||||||
|
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
||||||
|
BotToken: maputil.GetString(channelConfig, "apiToken"),
|
||||||
|
ChatId: maputil.GetInt64(channelConfig, "chatId"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeWebhook:
|
||||||
|
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "url"),
|
||||||
|
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeWeCom:
|
||||||
|
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unsupported notifier channel '%s'", channelConfig)
|
||||||
|
}
|
@ -8,25 +8,30 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/domain/dtos"
|
"github.com/usual2970/certimate/internal/domain/dtos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
const (
|
const (
|
||||||
notifyTestTitle = "测试通知"
|
notifyTestTitle = "测试通知"
|
||||||
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
|
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type settingsRepository interface {
|
type settingsRepository interface {
|
||||||
GetByName(ctx context.Context, name string) (*domain.Settings, error)
|
GetByName(ctx context.Context, name string) (*domain.Settings, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyService struct {
|
type NotifyService struct {
|
||||||
settingsRepo settingsRepository
|
settingsRepo settingsRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func NewNotifyService(settingsRepo settingsRepository) *NotifyService {
|
func NewNotifyService(settingsRepo settingsRepository) *NotifyService {
|
||||||
return &NotifyService{
|
return &NotifyService{
|
||||||
settingsRepo: settingsRepo,
|
settingsRepo: settingsRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error {
|
func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error {
|
||||||
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -137,6 +137,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
listListenersLimit := int32(100)
|
listListenersLimit := int32(100)
|
||||||
var listListenersToken *string = nil
|
var listListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listListenersReq := &alialb.ListListenersRequest{
|
listListenersReq := &alialb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
@ -166,6 +172,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||||
listListenersToken = nil
|
listListenersToken = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listListenersReq := &alialb.ListListenersRequest{
|
listListenersReq := &alialb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
@ -262,6 +274,12 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
|
|||||||
listListenerCertificatesLimit := int32(100)
|
listListenerCertificatesLimit := int32(100)
|
||||||
var listListenerCertificatesToken *string = nil
|
var listListenerCertificatesToken *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
|
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
|
||||||
NextToken: listListenerCertificatesToken,
|
NextToken: listListenerCertificatesToken,
|
||||||
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
||||||
|
@ -142,6 +142,12 @@ func (d *DeployerProvider) deployToCloudNative(ctx context.Context, certPEM stri
|
|||||||
listDomainsPageNumber := int32(1)
|
listDomainsPageNumber := int32(1)
|
||||||
listDomainsPageSize := int32(10)
|
listDomainsPageSize := int32(10)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listDomainsReq := &aliapig.ListDomainsRequest{
|
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||||
GatewayId: tea.String(d.config.GatewayId),
|
GatewayId: tea.String(d.config.GatewayId),
|
||||||
NameLike: tea.String(d.config.Domain),
|
NameLike: tea.String(d.config.Domain),
|
||||||
|
@ -126,8 +126,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
// 循环获取部署任务详情,等待任务状态变更
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
|
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
|
||||||
for {
|
for {
|
||||||
if ctx.Err() != nil {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
|
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
|
||||||
|
@ -132,6 +132,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
describeLoadBalancerListenersLimit := int32(100)
|
describeLoadBalancerListenersLimit := int32(100)
|
||||||
var describeLoadBalancerListenersToken *string = nil
|
var describeLoadBalancerListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
|
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
|
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
|
||||||
@ -166,8 +172,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerPort := range listenerPorts {
|
for _, listenerPort := range listenerPorts {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
listListenersLimit := int32(100)
|
listListenersLimit := int32(100)
|
||||||
var listListenersToken *string = nil
|
var listListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listListenersReq := &alinlb.ListListenersRequest{
|
listListenersReq := &alinlb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
@ -158,8 +164,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,16 +117,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
// 关联证书与加速域名
|
select {
|
||||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert
|
case <-ctx.Done():
|
||||||
batchDeployCertReq := &bpcdn.BatchDeployCertRequest{
|
return nil, ctx.Err()
|
||||||
CertId: upres.CertId,
|
|
||||||
Domain: domain,
|
default:
|
||||||
}
|
// 关联证书与加速域名
|
||||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert
|
||||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
batchDeployCertReq := &bpcdn.BatchDeployCertRequest{
|
||||||
if err != nil {
|
CertId: upres.CertId,
|
||||||
errs = append(errs, err)
|
Domain: domain,
|
||||||
|
}
|
||||||
|
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||||
|
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str
|
|||||||
listListenersLimit := int32(2000)
|
listListenersLimit := int32(2000)
|
||||||
var listListenersMarker *string = nil
|
var listListenersMarker *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listListenersReq := &hcelbmodel.ListListenersRequest{
|
listListenersReq := &hcelbmodel.ListListenersRequest{
|
||||||
Limit: typeutil.ToPtr(listListenersLimit),
|
Limit: typeutil.ToPtr(listListenersLimit),
|
||||||
Marker: listListenersMarker,
|
Marker: listListenersMarker,
|
||||||
@ -201,8 +207,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,12 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPEM stri
|
|||||||
listHostPage := int32(1)
|
listHostPage := int32(1)
|
||||||
listHostPageSize := int32(100)
|
listHostPageSize := int32(100)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listHostReq := &hcwafmodel.ListHostRequest{
|
listHostReq := &hcwafmodel.ListHostRequest{
|
||||||
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
|
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
|
||||||
Page: typeutil.ToPtr(listHostPage),
|
Page: typeutil.ToPtr(listHostPage),
|
||||||
@ -239,6 +245,12 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPEM stri
|
|||||||
listPremiumHostPage := int32(1)
|
listPremiumHostPage := int32(1)
|
||||||
listPremiumHostPageSize := int32(100)
|
listPremiumHostPageSize := int32(100)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listPremiumHostReq := &hcwafmodel.ListPremiumHostRequest{
|
listPremiumHostReq := &hcwafmodel.ListPremiumHostRequest{
|
||||||
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
|
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
|
||||||
Page: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPage)),
|
Page: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPage)),
|
||||||
|
@ -132,6 +132,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
describeListenersPageNumber := 1
|
describeListenersPageNumber := 1
|
||||||
describeListenersPageSize := 100
|
describeListenersPageSize := 100
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
describeListenersReq := jdlbapi.NewDescribeListenersRequest(d.config.RegionId)
|
describeListenersReq := jdlbapi.NewDescribeListenersRequest(d.config.RegionId)
|
||||||
describeListenersReq.SetFilters([]jdcommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}})
|
describeListenersReq.SetFilters([]jdcommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}})
|
||||||
describeListenersReq.SetPageSize(describeListenersPageNumber)
|
describeListenersReq.SetPageSize(describeListenersPageNumber)
|
||||||
@ -164,8 +170,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,12 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
listDomainsPageNumber := 1
|
listDomainsPageNumber := 1
|
||||||
listDomainsPageSize := 100
|
listDomainsPageSize := 100
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listDomainsReq := jdvodapi.NewListDomainsRequest()
|
listDomainsReq := jdvodapi.NewListDomainsRequest()
|
||||||
listDomainsReq.SetPageNumber(1)
|
listDomainsReq.SetPageNumber(1)
|
||||||
listDomainsReq.SetPageSize(100)
|
listDomainsReq.SetPageSize(100)
|
||||||
|
@ -188,8 +188,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.modifyListenerCertificate(ctx, d.config.LoadbalancerId, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.modifyListenerCertificate(ctx, d.config.LoadbalancerId, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +108,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
// 循环获取部署任务详情,等待任务状态变更
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
||||||
for {
|
for {
|
||||||
if ctx.Err() != nil {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
|
@ -132,6 +132,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
describeListenersPageSize := int64(100)
|
describeListenersPageSize := int64(100)
|
||||||
describeListenersPageNumber := int64(1)
|
describeListenersPageNumber := int64(1)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
describeListenersReq := &vealb.DescribeListenersInput{
|
describeListenersReq := &vealb.DescribeListenersInput{
|
||||||
LoadBalancerId: ve.String(d.config.LoadbalancerId),
|
LoadBalancerId: ve.String(d.config.LoadbalancerId),
|
||||||
Protocol: ve.String("HTTPS"),
|
Protocol: ve.String("HTTPS"),
|
||||||
@ -163,8 +169,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,16 +117,21 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
// 关联证书与加速域名
|
select {
|
||||||
// REF: https://www.volcengine.com/docs/6454/125712
|
case <-ctx.Done():
|
||||||
batchDeployCertReq := &vecdn.BatchDeployCertRequest{
|
return nil, ctx.Err()
|
||||||
CertId: upres.CertId,
|
default:
|
||||||
Domain: domain,
|
// 关联证书与加速域名
|
||||||
}
|
// REF: https://www.volcengine.com/docs/6454/125712
|
||||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
batchDeployCertReq := &vecdn.BatchDeployCertRequest{
|
||||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
CertId: upres.CertId,
|
||||||
if err != nil {
|
Domain: domain,
|
||||||
errs = append(errs, err)
|
}
|
||||||
|
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||||
|
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +128,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
describeListenersPageSize := int64(100)
|
describeListenersPageSize := int64(100)
|
||||||
describeListenersPageNumber := int64(1)
|
describeListenersPageNumber := int64(1)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
describeListenersReq := &veclb.DescribeListenersInput{
|
describeListenersReq := &veclb.DescribeListenersInput{
|
||||||
LoadBalancerId: ve.String(d.config.LoadbalancerId),
|
LoadBalancerId: ve.String(d.config.LoadbalancerId),
|
||||||
Protocol: ve.String("HTTPS"),
|
Protocol: ve.String("HTTPS"),
|
||||||
@ -159,8 +165,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,17 +125,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
// 绑定证书
|
select {
|
||||||
// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6
|
case <-ctx.Done():
|
||||||
bindCertReq := &velive.BindCertBody{
|
return nil, ctx.Err()
|
||||||
ChainID: upres.CertId,
|
default:
|
||||||
Domain: domain,
|
// 绑定证书
|
||||||
HTTPS: ve.Bool(true),
|
// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6
|
||||||
}
|
bindCertReq := &velive.BindCertBody{
|
||||||
bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq)
|
ChainID: upres.CertId,
|
||||||
d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp))
|
Domain: domain,
|
||||||
if err != nil {
|
HTTPS: ve.Bool(true),
|
||||||
errs = append(errs, err)
|
}
|
||||||
|
bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq)
|
||||||
|
d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp))
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +199,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
wangsuTaskId = wangsuTaskMatches[1]
|
wangsuTaskId = wangsuTaskMatches[1]
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
if ctx.Err() != nil {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
|
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -18,8 +20,13 @@ import (
|
|||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
// Webhook URL。
|
// Webhook URL。
|
||||||
WebhookUrl string `json:"webhookUrl"`
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
// Webhook 回调数据(JSON 格式)。
|
// Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。
|
||||||
WebhookData string `json:"webhookData,omitempty"`
|
WebhookData string `json:"webhookData,omitempty"`
|
||||||
|
// 请求谓词。
|
||||||
|
// 零值时默认为 "POST"。
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
// 请求标头。
|
||||||
|
Headers map[string]string `json:"headers,omitempty"`
|
||||||
// 是否允许不安全的连接。
|
// 是否允许不安全的连接。
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
@ -62,31 +69,111 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 解析证书内容
|
||||||
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse x509: %w", err)
|
return nil, fmt.Errorf("failed to parse x509: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var webhookData interface{}
|
// 处理 Webhook URL
|
||||||
err = json.Unmarshal([]byte(d.config.WebhookData), &webhookData)
|
webhookUrl, err := url.Parse(d.config.WebhookUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshall webhook data: %w", err)
|
return nil, fmt.Errorf("failed to parse webhook url: %w", err)
|
||||||
|
} else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme)
|
||||||
|
} else {
|
||||||
|
webhookUrl.Path = strings.ReplaceAll(webhookUrl.Path, "${DOMAIN}", url.PathEscape(certX509.Subject.CommonName))
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceJsonValueRecursively(webhookData, "${DOMAIN}", certX509.Subject.CommonName)
|
// 处理 Webhook 请求谓词
|
||||||
replaceJsonValueRecursively(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";"))
|
webhookMethod := strings.ToUpper(d.config.Method)
|
||||||
replaceJsonValueRecursively(webhookData, "${SUBJECT_ALT_NAMES}", strings.Join(certX509.DNSNames, ";"))
|
if webhookMethod == "" {
|
||||||
replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPEM)
|
webhookMethod = http.MethodPost
|
||||||
replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPEM)
|
} else if webhookMethod != http.MethodGet &&
|
||||||
|
webhookMethod != http.MethodPost &&
|
||||||
|
webhookMethod != http.MethodPut &&
|
||||||
|
webhookMethod != http.MethodPatch &&
|
||||||
|
webhookMethod != http.MethodDelete {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := d.httpClient.R().
|
// 处理 Webhook 请求标头
|
||||||
|
webhookHeaders := make(http.Header)
|
||||||
|
for k, v := range d.config.Headers {
|
||||||
|
webhookHeaders.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求内容类型
|
||||||
|
const CONTENT_TYPE_JSON = "application/json"
|
||||||
|
const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"
|
||||||
|
const CONTENT_TYPE_MULTIPART = "multipart/form-data"
|
||||||
|
webhookContentType := webhookHeaders.Get("Content-Type")
|
||||||
|
if webhookContentType == "" {
|
||||||
|
webhookContentType = CONTENT_TYPE_JSON
|
||||||
|
webhookHeaders.Set("Content-Type", CONTENT_TYPE_JSON)
|
||||||
|
} else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) &&
|
||||||
|
strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) &&
|
||||||
|
strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求数据
|
||||||
|
var webhookData interface{}
|
||||||
|
if d.config.WebhookData == "" {
|
||||||
|
webhookData = map[string]string{
|
||||||
|
"name": strings.Join(certX509.DNSNames, ";"),
|
||||||
|
"cert": certPEM,
|
||||||
|
"privkey": privkeyPEM,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = json.Unmarshal([]byte(d.config.WebhookData), &webhookData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceJsonValueRecursively(webhookData, "${DOMAIN}", certX509.Subject.CommonName)
|
||||||
|
replaceJsonValueRecursively(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";"))
|
||||||
|
replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPEM)
|
||||||
|
replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPEM)
|
||||||
|
|
||||||
|
if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART {
|
||||||
|
temp := make(map[string]string)
|
||||||
|
jsonb, err := json.Marshal(webhookData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
} else if err := json.Unmarshal(jsonb, &temp); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
} else {
|
||||||
|
webhookData = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成请求
|
||||||
|
// 其中 GET 请求需转换为查询参数
|
||||||
|
req := d.httpClient.R().
|
||||||
SetContext(ctx).
|
SetContext(ctx).
|
||||||
SetHeader("Content-Type", "application/json").
|
SetHeaderMultiValues(webhookHeaders)
|
||||||
SetBody(webhookData).
|
req.URL = webhookUrl.String()
|
||||||
Post(d.config.WebhookUrl)
|
req.Method = webhookMethod
|
||||||
|
if webhookMethod == http.MethodGet {
|
||||||
|
req.SetQueryParams(webhookData.(map[string]string))
|
||||||
|
} else {
|
||||||
|
switch webhookContentType {
|
||||||
|
case CONTENT_TYPE_JSON:
|
||||||
|
req.SetBody(webhookData)
|
||||||
|
case CONTENT_TYPE_FORM:
|
||||||
|
req.SetFormData(webhookData.(map[string]string))
|
||||||
|
case CONTENT_TYPE_MULTIPART:
|
||||||
|
req.SetMultipartFormData(webhookData.(map[string]string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := req.SetDebug(true).Send()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
||||||
} else if resp.StatusCode() != 200 {
|
} else if resp.IsError() {
|
||||||
return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode())
|
return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,10 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fInputCertPath string
|
fInputCertPath string
|
||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fWebhookUrl string
|
fWebhookUrl string
|
||||||
fWebhookData string
|
fWebhookContentType string
|
||||||
|
fWebhookData string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -24,6 +25,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "")
|
flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "")
|
||||||
|
flag.StringVar(&fWebhookContentType, argsPrefix+"CONTENTTYPE", "application/json", "")
|
||||||
flag.StringVar(&fWebhookData, argsPrefix+"DATA", "", "")
|
flag.StringVar(&fWebhookData, argsPrefix+"DATA", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +36,8 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_DEPLOYER_WEBHOOK_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_DEPLOYER_WEBHOOK_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_DEPLOYER_WEBHOOK_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_DEPLOYER_WEBHOOK_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_DEPLOYER_WEBHOOK_URL="https://example.com/your-webhook-url" \
|
--CERTIMATE_DEPLOYER_WEBHOOK_URL="https://example.com/your-webhook-url" \
|
||||||
--CERTIMATE_DEPLOYER_WEBHOOK_DATA="{\"certificate\":\"${Certificate}\",\"privateKey\":\"${PrivateKey}\"}"
|
--CERTIMATE_DEPLOYER_WEBHOOK_CONTENTTYPE="application/json" \
|
||||||
|
--CERTIMATE_DEPLOYER_WEBHOOK_DATA="{\"certificate\":\"${CERTIFICATE}\",\"privateKey\":\"${PRIVATE_KEY}\"}"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -45,12 +48,17 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
||||||
|
fmt.Sprintf("WEBHOOKCONTENTTYPE: %v", fWebhookContentType),
|
||||||
fmt.Sprintf("WEBHOOKDATA: %v", fWebhookData),
|
fmt.Sprintf("WEBHOOKDATA: %v", fWebhookData),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
WebhookUrl: fWebhookUrl,
|
WebhookUrl: fWebhookUrl,
|
||||||
WebhookData: fWebhookData,
|
WebhookData: fWebhookData,
|
||||||
|
Method: "POST",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Content-Type": fWebhookContentType,
|
||||||
|
},
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -32,6 +32,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ package dingtalk
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/dingding"
|
"github.com/nikoksr/notify/service/dingding"
|
||||||
|
|
||||||
@ -10,8 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// 钉钉机器人的 Token。
|
// 钉钉机器人的 Webhook 地址。
|
||||||
AccessToken string `json:"accessToken"`
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
// 钉钉机器人的 Secret。
|
// 钉钉机器人的 Secret。
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
}
|
}
|
||||||
@ -30,6 +32,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +46,13 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
|
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid webhook url: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
srv := dingding.New(&dingding.Config{
|
srv := dingding.New(&dingding.Config{
|
||||||
Token: n.config.AccessToken,
|
Token: webhookUrl.Query().Get("access_token"),
|
||||||
Secret: n.config.Secret,
|
Secret: n.config.Secret,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ type NotifierConfig struct {
|
|||||||
// 零值时根据是否启用 TLS 决定。
|
// 零值时根据是否启用 TLS 决定。
|
||||||
SmtpPort int32 `json:"smtpPort"`
|
SmtpPort int32 `json:"smtpPort"`
|
||||||
// 是否启用 TLS。
|
// 是否启用 TLS。
|
||||||
SmtpTLS bool `json:"smtpTLS"`
|
SmtpTls bool `json:"smtpTls"`
|
||||||
// 用户名。
|
// 用户名。
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
// 密码。
|
// 密码。
|
||||||
@ -44,6 +44,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
|
|
||||||
var smtpAddr string
|
var smtpAddr string
|
||||||
if n.config.SmtpPort == 0 {
|
if n.config.SmtpPort == 0 {
|
||||||
if n.config.SmtpTLS {
|
if n.config.SmtpTls {
|
||||||
smtpAddr = fmt.Sprintf("%s:465", n.config.SmtpHost)
|
smtpAddr = fmt.Sprintf("%s:465", n.config.SmtpHost)
|
||||||
} else {
|
} else {
|
||||||
smtpAddr = fmt.Sprintf("%s:25", n.config.SmtpHost)
|
smtpAddr = fmt.Sprintf("%s:25", n.config.SmtpHost)
|
||||||
@ -74,7 +75,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
}
|
}
|
||||||
|
|
||||||
var yak *mailyak.MailYak
|
var yak *mailyak.MailYak
|
||||||
if n.config.SmtpTLS {
|
if n.config.SmtpTls {
|
||||||
yak, err = mailyak.NewWithTLS(smtpAddr, smtpAuth, newTlsConfig())
|
yak, err = mailyak.NewWithTLS(smtpAddr, smtpAuth, newTlsConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -67,7 +67,7 @@ func TestNotify(t *testing.T) {
|
|||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
SmtpHost: fSmtpHost,
|
SmtpHost: fSmtpHost,
|
||||||
SmtpPort: int32(fSmtpPort),
|
SmtpPort: int32(fSmtpPort),
|
||||||
SmtpTLS: fSmtpTLS,
|
SmtpTls: fSmtpTLS,
|
||||||
Username: fUsername,
|
Username: fUsername,
|
||||||
Password: fPassword,
|
Password: fPassword,
|
||||||
SenderAddress: fSenderAddress,
|
SenderAddress: fSenderAddress,
|
||||||
|
@ -9,25 +9,21 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// Gotify 服务地址
|
// Gotify 服务地址。
|
||||||
// 示例:https://gotify.example.com
|
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
// Gotify Token
|
// Gotify Token。
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
// Gotify 消息优先级
|
// Gotify 消息优先级。
|
||||||
Priority int64 `json:"priority"`
|
Priority int64 `json:"priority,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
// 未来将移除
|
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +36,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: http.DefaultClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -54,7 +51,6 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
// Gotify 原生实现, notify 库没有实现, 等待合并
|
|
||||||
reqBody := &struct {
|
reqBody := &struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
@ -65,10 +61,9 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
Priority: n.config.Priority,
|
Priority: n.config.Priority,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make request
|
|
||||||
body, err := json.Marshal(reqBody)
|
body, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "encode message body")
|
return nil, fmt.Errorf("gotify api error: failed to encode message body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
req, err := http.NewRequestWithContext(
|
||||||
@ -78,27 +73,24 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
bytes.NewReader(body),
|
bytes.NewReader(body),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "create new request")
|
return nil, fmt.Errorf("gotify api error: failed to create new request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", n.config.Token))
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", n.config.Token))
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
// Send request to gotify service
|
|
||||||
resp, err := n.httpClient.Do(req)
|
resp, err := n.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "send request to gotify server")
|
return nil, fmt.Errorf("gotify api error: failed to send request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Read response and verify success
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
result, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "read response")
|
return nil, fmt.Errorf("gotify api error: failed to read response: %w", err)
|
||||||
|
} else if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("gotify returned status code %d: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,20 +7,21 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/mattermost"
|
"github.com/nikoksr/notify/service/mattermost"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// Mattermost 服务地址。
|
// 服务地址。
|
||||||
ServerUrl string `json:"serverUrl"`
|
ServerUrl string `json:"serverUrl"`
|
||||||
// 频道ID
|
// 用户名。
|
||||||
ChannelId string `json:"channelId"`
|
|
||||||
// 用户名
|
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
// 密码
|
// 密码。
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
// 频道 ID。
|
||||||
|
ChannelId string `json:"channelId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
@ -37,6 +38,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := mattermost.New(n.config.ServerUrl)
|
srv := mattermost.New(strings.TrimRight(n.config.ServerUrl, "/"))
|
||||||
|
|
||||||
if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil {
|
if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -9,20 +9,19 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
Token string `json:"token"` // 应用 API Token
|
// Pushover API Token。
|
||||||
User string `json:"user"` // 用户/分组 Key
|
Token string `json:"token"`
|
||||||
|
// 用户或分组标识。
|
||||||
|
User string `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
// 未来将移除
|
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +34,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: http.DefaultClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -48,10 +48,8 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify 发送通知
|
|
||||||
// 参考文档:https://pushover.net/api
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
// 请求体
|
// REF: https://pushover.net/api
|
||||||
reqBody := &struct {
|
reqBody := &struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
@ -64,10 +62,9 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
Message: message,
|
Message: message,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make request
|
|
||||||
body, err := json.Marshal(reqBody)
|
body, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "encode message body")
|
return nil, fmt.Errorf("pushover api error: failed to encode message body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
req, err := http.NewRequestWithContext(
|
||||||
@ -77,25 +74,22 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
bytes.NewReader(body),
|
bytes.NewReader(body),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "create new request")
|
return nil, fmt.Errorf("pushover api error: failed to create new request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
// Send request to pushover service
|
|
||||||
resp, err := n.httpClient.Do(req)
|
resp, err := n.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "send request to pushover server")
|
return nil, fmt.Errorf("pushover api error: failed to send request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
result, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "read response")
|
return nil, fmt.Errorf("pushover api error: failed to read response: %w", err)
|
||||||
}
|
} else if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("pushover returned status code %d: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -9,20 +9,17 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// PushPlus Token
|
// PushPlus Token。
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
// 未来将移除
|
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +32,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: http.DefaultClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -48,10 +46,8 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify 发送通知
|
|
||||||
// 参考文档:https://pushplus.plus/doc/guide/api.html
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
// 请求体
|
// REF: https://pushplus.plus/doc/guide/api.html
|
||||||
reqBody := &struct {
|
reqBody := &struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
@ -62,10 +58,9 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
Content: message,
|
Content: message,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make request
|
|
||||||
body, err := json.Marshal(reqBody)
|
body, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "encode message body")
|
return nil, fmt.Errorf("pushplus api error: failed to encode message body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
req, err := http.NewRequestWithContext(
|
||||||
@ -75,38 +70,32 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
bytes.NewReader(body),
|
bytes.NewReader(body),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "create new request")
|
return nil, fmt.Errorf("pushplus api error: failed to create new request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
// Send request to pushplus service
|
|
||||||
resp, err := n.httpClient.Do(req)
|
resp, err := n.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "send request to pushplus server")
|
return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
result, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "read response")
|
return nil, fmt.Errorf("pushplus api error: failed to read response: %w", err)
|
||||||
|
} else if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("pushplus returned status code %d: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析响应
|
|
||||||
var errorResponse struct {
|
var errorResponse struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(result, &errorResponse); err != nil {
|
if err := json.Unmarshal(result, &errorResponse); err != nil {
|
||||||
return nil, errors.Wrap(err, "decode response")
|
return nil, fmt.Errorf("pushplus api error: failed to decode response: %w", err)
|
||||||
}
|
} else if errorResponse.Code != 200 {
|
||||||
|
return nil, fmt.Errorf("pushplus api error: unexpected response code: %d, msg: %s", errorResponse.Code, errorResponse.Msg)
|
||||||
if errorResponse.Code != 200 {
|
|
||||||
return nil, fmt.Errorf("pushplus returned error: %s", errorResponse.Msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -29,6 +29,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// Telegram API Token。
|
// Telegram Bot API Token。
|
||||||
ApiToken string `json:"apiToken"`
|
BotToken string `json:"botToken"`
|
||||||
// Telegram Chat ID。
|
// Telegram Chat ID。
|
||||||
ChatId int64 `json:"chatId"`
|
ChatId int64 `json:"chatId"`
|
||||||
}
|
}
|
||||||
@ -30,6 +30,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv, err := telegram.New(n.config.ApiToken)
|
srv, err := telegram.New(n.config.BotToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func TestNotify(t *testing.T) {
|
|||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
ApiToken: fApiToken,
|
BotToken: fApiToken,
|
||||||
ChatId: fChartId,
|
ChatId: fChartId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,24 +3,37 @@ package webhook
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
webhook "github.com/nikoksr/notify/service/http"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// Webhook URL。
|
// Webhook URL。
|
||||||
Url string `json:"url"`
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
|
// Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。
|
||||||
|
WebhookData string `json:"webhookData,omitempty"`
|
||||||
|
// 请求谓词。
|
||||||
|
// 零值时默认为 "POST"。
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
// 请求标头。
|
||||||
|
Headers map[string]string `json:"headers,omitempty"`
|
||||||
// 是否允许不安全的连接。
|
// 是否允许不安全的连接。
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -30,8 +43,18 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New().
|
||||||
|
SetTimeout(30 * time.Second).
|
||||||
|
SetRetryCount(3).
|
||||||
|
SetRetryWaitTime(5 * time.Second)
|
||||||
|
if config.AllowInsecureConnections {
|
||||||
|
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,20 +68,120 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := webhook.New()
|
// 处理 Webhook URL
|
||||||
srv.AddReceiversURLs(n.config.Url)
|
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
||||||
|
|
||||||
if n.config.AllowInsecureConnections {
|
|
||||||
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
|
||||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
|
||||||
client := &http.Client{Transport: transport}
|
|
||||||
srv.WithClient(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to parse webhook url: %w", err)
|
||||||
|
} else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求谓词
|
||||||
|
webhookMethod := strings.ToUpper(n.config.Method)
|
||||||
|
if webhookMethod == "" {
|
||||||
|
webhookMethod = http.MethodPost
|
||||||
|
} else if webhookMethod != http.MethodGet &&
|
||||||
|
webhookMethod != http.MethodPost &&
|
||||||
|
webhookMethod != http.MethodPut &&
|
||||||
|
webhookMethod != http.MethodPatch &&
|
||||||
|
webhookMethod != http.MethodDelete {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求标头
|
||||||
|
webhookHeaders := make(http.Header)
|
||||||
|
for k, v := range n.config.Headers {
|
||||||
|
webhookHeaders.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求内容类型
|
||||||
|
const CONTENT_TYPE_JSON = "application/json"
|
||||||
|
const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"
|
||||||
|
const CONTENT_TYPE_MULTIPART = "multipart/form-data"
|
||||||
|
webhookContentType := webhookHeaders.Get("Content-Type")
|
||||||
|
if webhookContentType == "" {
|
||||||
|
webhookContentType = CONTENT_TYPE_JSON
|
||||||
|
webhookHeaders.Set("Content-Type", CONTENT_TYPE_JSON)
|
||||||
|
} else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) &&
|
||||||
|
strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) &&
|
||||||
|
strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) {
|
||||||
|
return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 Webhook 请求数据
|
||||||
|
var webhookData interface{}
|
||||||
|
if n.config.WebhookData == "" {
|
||||||
|
webhookData = map[string]string{
|
||||||
|
"subject": subject,
|
||||||
|
"message": message,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = json.Unmarshal([]byte(n.config.WebhookData), &webhookData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceJsonValueRecursively(webhookData, "${SUBJECT}", subject)
|
||||||
|
replaceJsonValueRecursively(webhookData, "${MESSAGE}", message)
|
||||||
|
|
||||||
|
if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART {
|
||||||
|
temp := make(map[string]string)
|
||||||
|
jsonb, err := json.Marshal(webhookData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
} else if err := json.Unmarshal(jsonb, &temp); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||||
|
} else {
|
||||||
|
webhookData = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成请求
|
||||||
|
// 其中 GET 请求需转换为查询参数
|
||||||
|
req := n.httpClient.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetHeaderMultiValues(webhookHeaders)
|
||||||
|
req.URL = webhookUrl.String()
|
||||||
|
req.Method = webhookMethod
|
||||||
|
if webhookMethod == http.MethodGet {
|
||||||
|
req.SetQueryParams(webhookData.(map[string]string))
|
||||||
|
} else {
|
||||||
|
switch webhookContentType {
|
||||||
|
case CONTENT_TYPE_JSON:
|
||||||
|
req.SetBody(webhookData)
|
||||||
|
case CONTENT_TYPE_FORM:
|
||||||
|
req.SetFormData(webhookData.(map[string]string))
|
||||||
|
case CONTENT_TYPE_MULTIPART:
|
||||||
|
req.SetMultipartFormData(webhookData.(map[string]string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := req.Send()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
||||||
|
} else if resp.IsError() {
|
||||||
|
return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
n.logger.Debug("webhook responded", slog.Any("response", resp.String()))
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func replaceJsonValueRecursively(data interface{}, oldStr, newStr string) interface{} {
|
||||||
|
switch v := data.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
for k, val := range v {
|
||||||
|
v[k] = replaceJsonValueRecursively(val, oldStr, newStr)
|
||||||
|
}
|
||||||
|
case []any:
|
||||||
|
for i, val := range v {
|
||||||
|
v[i] = replaceJsonValueRecursively(val, oldStr, newStr)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
return strings.ReplaceAll(v, oldStr, newStr)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
@ -15,19 +15,24 @@ const (
|
|||||||
mockMessage = "test_message"
|
mockMessage = "test_message"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fUrl string
|
var (
|
||||||
|
fWebhookUrl string
|
||||||
|
fWebhookContentType string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_NOTIFIER_WEBHOOK_"
|
argsPrefix := "CERTIMATE_NOTIFIER_WEBHOOK_"
|
||||||
|
|
||||||
flag.StringVar(&fUrl, argsPrefix+"URL", "", "")
|
flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "")
|
||||||
|
flag.StringVar(&fWebhookContentType, argsPrefix+"CONTENTTYPE", "application/json", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./webhook_test.go -args \
|
go test -v ./webhook_test.go -args \
|
||||||
--CERTIMATE_NOTIFIER_WEBHOOK_URL="https://example.com/your-webhook-url"
|
--CERTIMATE_NOTIFIER_WEBHOOK_URL="https://example.com/your-webhook-url" \
|
||||||
|
--CERTIMATE_NOTIFIER_WEBHOOK_CONTENTTYPE="application/json"
|
||||||
*/
|
*/
|
||||||
func TestNotify(t *testing.T) {
|
func TestNotify(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -35,11 +40,15 @@ func TestNotify(t *testing.T) {
|
|||||||
t.Run("Notify", func(t *testing.T) {
|
t.Run("Notify", func(t *testing.T) {
|
||||||
t.Log(strings.Join([]string{
|
t.Log(strings.Join([]string{
|
||||||
"args:",
|
"args:",
|
||||||
fmt.Sprintf("URL: %v", fUrl),
|
fmt.Sprintf("URL: %v", fWebhookUrl),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
Url: fUrl,
|
WebhookUrl: fWebhookUrl,
|
||||||
|
Method: "POST",
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Content-Type": fWebhookContentType,
|
||||||
|
},
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,6 +93,12 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
|
|||||||
searchWebsiteSSLPageNumber := int32(1)
|
searchWebsiteSSLPageNumber := int32(1)
|
||||||
searchWebsiteSSLPageSize := int32(100)
|
searchWebsiteSSLPageSize := int32(100)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{
|
searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{
|
||||||
Page: searchWebsiteSSLPageNumber,
|
Page: searchWebsiteSSLPageNumber,
|
||||||
PageSize: searchWebsiteSSLPageSize,
|
PageSize: searchWebsiteSSLPageSize,
|
||||||
|
@ -71,6 +71,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
listUserCertificateOrderPage := int64(1)
|
listUserCertificateOrderPage := int64(1)
|
||||||
listUserCertificateOrderLimit := int64(50)
|
listUserCertificateOrderLimit := int64(50)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listUserCertificateOrderReq := &alicas.ListUserCertificateOrderRequest{
|
listUserCertificateOrderReq := &alicas.ListUserCertificateOrderRequest{
|
||||||
CurrentPage: tea.Int64(listUserCertificateOrderPage),
|
CurrentPage: tea.Int64(listUserCertificateOrderPage),
|
||||||
ShowSize: tea.Int64(listUserCertificateOrderLimit),
|
ShowSize: tea.Int64(listUserCertificateOrderLimit),
|
||||||
|
@ -74,6 +74,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
var listCertificatesNextToken *string = nil
|
var listCertificatesNextToken *string = nil
|
||||||
listCertificatesMaxItems := int32(1000)
|
listCertificatesMaxItems := int32(1000)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertificatesReq := &awsacm.ListCertificatesInput{
|
listCertificatesReq := &awsacm.ListCertificatesInput{
|
||||||
NextToken: listCertificatesNextToken,
|
NextToken: listCertificatesNextToken,
|
||||||
MaxItems: aws.Int32(listCertificatesMaxItems),
|
MaxItems: aws.Int32(listCertificatesMaxItems),
|
||||||
|
@ -74,6 +74,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
Source: bytepluscdn.GetStrPtr("cert_center"),
|
Source: bytepluscdn.GetStrPtr("cert_center"),
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq)
|
listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq)
|
||||||
u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp))
|
u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,6 +76,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
listCertificatesLimit := int32(2000)
|
listCertificatesLimit := int32(2000)
|
||||||
var listCertificatesMarker *string = nil
|
var listCertificatesMarker *string = nil
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertificatesReq := &hcelbmodel.ListCertificatesRequest{
|
listCertificatesReq := &hcelbmodel.ListCertificatesRequest{
|
||||||
Limit: typeutil.ToPtr(listCertificatesLimit),
|
Limit: typeutil.ToPtr(listCertificatesLimit),
|
||||||
Marker: listCertificatesMarker,
|
Marker: listCertificatesMarker,
|
||||||
|
@ -72,6 +72,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
listCertificatesLimit := int32(50)
|
listCertificatesLimit := int32(50)
|
||||||
listCertificatesOffset := int32(0)
|
listCertificatesOffset := int32(0)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertificatesReq := &hcscmmodel.ListCertificatesRequest{
|
listCertificatesReq := &hcscmmodel.ListCertificatesRequest{
|
||||||
Limit: typeutil.ToPtr(listCertificatesLimit),
|
Limit: typeutil.ToPtr(listCertificatesLimit),
|
||||||
Offset: typeutil.ToPtr(listCertificatesOffset),
|
Offset: typeutil.ToPtr(listCertificatesOffset),
|
||||||
|
@ -77,6 +77,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
listCertificatesPage := int32(1)
|
listCertificatesPage := int32(1)
|
||||||
listCertificatesPageSize := int32(100)
|
listCertificatesPageSize := int32(100)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertificatesReq := &hcwafmodel.ListCertificatesRequest{
|
listCertificatesReq := &hcwafmodel.ListCertificatesRequest{
|
||||||
Page: typeutil.ToPtr(listCertificatesPage),
|
Page: typeutil.ToPtr(listCertificatesPage),
|
||||||
Pagesize: typeutil.ToPtr(listCertificatesPageSize),
|
Pagesize: typeutil.ToPtr(listCertificatesPageSize),
|
||||||
|
@ -77,6 +77,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
describeCertsPageNumber := 1
|
describeCertsPageNumber := 1
|
||||||
describeCertsPageSize := 10
|
describeCertsPageSize := 10
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
describeCertsReq := jdsslapi.NewDescribeCertsRequest()
|
describeCertsReq := jdsslapi.NewDescribeCertsRequest()
|
||||||
describeCertsReq.SetDomainName(certX509.Subject.CommonName)
|
describeCertsReq.SetDomainName(certX509.Subject.CommonName)
|
||||||
describeCertsReq.SetPageNumber(describeCertsPageNumber)
|
describeCertsReq.SetPageNumber(describeCertsPageNumber)
|
||||||
|
@ -93,6 +93,12 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string)
|
|||||||
sslCenterListPage := int32(1)
|
sslCenterListPage := int32(1)
|
||||||
sslCenterListPerPage := int32(100)
|
sslCenterListPerPage := int32(100)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
sslCenterListReq := &rainyunsdk.SslCenterListRequest{
|
sslCenterListReq := &rainyunsdk.SslCenterListRequest{
|
||||||
Filters: &rainyunsdk.SslCenterListFilters{
|
Filters: &rainyunsdk.SslCenterListFilters{
|
||||||
Domain: &certX509.Subject.CommonName,
|
Domain: &certX509.Subject.CommonName,
|
||||||
|
@ -124,6 +124,12 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string)
|
|||||||
getCertificateListPage := int(1)
|
getCertificateListPage := int(1)
|
||||||
getCertificateListLimit := int(1000)
|
getCertificateListLimit := int(1000)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
getCertificateListReq := u.sdkClient.NewGetCertificateListRequest()
|
getCertificateListReq := u.sdkClient.NewGetCertificateListRequest()
|
||||||
getCertificateListReq.Mode = ucloud.String("trust")
|
getCertificateListReq.Mode = ucloud.String("trust")
|
||||||
getCertificateListReq.Domain = ucloud.String(certX509.Subject.CommonName)
|
getCertificateListReq.Domain = ucloud.String(certX509.Subject.CommonName)
|
||||||
|
@ -75,6 +75,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
Source: "volc_cert_center",
|
Source: "volc_cert_center",
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq)
|
listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq)
|
||||||
u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp))
|
u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
33
internal/pkg/utils/http/parser.go
Normal file
33
internal/pkg/utils/http/parser.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 从表示 HTTP 标头的字符串解析并返回一个 http.Header 对象。
|
||||||
|
//
|
||||||
|
// 入参:
|
||||||
|
// - headers: 表示 HTTP 标头的字符串。
|
||||||
|
//
|
||||||
|
// 出参:
|
||||||
|
// - header: http.Header 对象。
|
||||||
|
// - err: 错误。
|
||||||
|
func ParseHeaders(headers string) (http.Header, error) {
|
||||||
|
str := strings.TrimSpace(headers) + "\r\n\r\n"
|
||||||
|
if len(str) == 4 {
|
||||||
|
return make(http.Header), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
br := bufio.NewReader(strings.NewReader(str))
|
||||||
|
tp := textproto.NewReader(br)
|
||||||
|
|
||||||
|
mimeHeader, err := tp.ReadMIMEHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.Header(mimeHeader), err
|
||||||
|
}
|
@ -199,6 +199,28 @@ func GetOrDefaultBool(dict map[string]any, key string, defaultValue bool) bool {
|
|||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 以 `map[string]V` 形式从字典中获取指定键的值。
|
||||||
|
//
|
||||||
|
// 入参:
|
||||||
|
// - dict: 字典。
|
||||||
|
// - key: 键。
|
||||||
|
//
|
||||||
|
// 出参:
|
||||||
|
// - 字典中键对应的 `map[string]V` 对象。
|
||||||
|
func GetKVMap[V any](dict map[string]any, key string) map[string]V {
|
||||||
|
if dict == nil {
|
||||||
|
return make(map[string]V)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := dict[key]; ok {
|
||||||
|
if result, ok := val.(map[string]V); ok {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return make(map[string]V)
|
||||||
|
}
|
||||||
|
|
||||||
// 以 `map[string]any` 形式从字典中获取指定键的值。
|
// 以 `map[string]any` 形式从字典中获取指定键的值。
|
||||||
//
|
//
|
||||||
// 入参:
|
// 入参:
|
||||||
@ -207,16 +229,6 @@ func GetOrDefaultBool(dict map[string]any, key string, defaultValue bool) bool {
|
|||||||
//
|
//
|
||||||
// 出参:
|
// 出参:
|
||||||
// - 字典中键对应的 `map[string]any` 对象。
|
// - 字典中键对应的 `map[string]any` 对象。
|
||||||
func GetAnyMap(dict map[string]any, key string) map[string]any {
|
func GetKVMapAny(dict map[string]any, key string) map[string]any {
|
||||||
if dict == nil {
|
return GetKVMap[any](dict, key)
|
||||||
return make(map[string]any)
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := dict[key]; ok {
|
|
||||||
if result, ok := val.(map[string]any); ok {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return make(map[string]any)
|
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ func (r *AccessRepository) castRecordToModel(record *core.Record) (*domain.Acces
|
|||||||
Name: record.GetString("name"),
|
Name: record.GetString("name"),
|
||||||
Provider: record.GetString("provider"),
|
Provider: record.GetString("provider"),
|
||||||
Config: config,
|
Config: config,
|
||||||
|
Reserve: record.GetString("reserve"),
|
||||||
}
|
}
|
||||||
return access, nil
|
return access, nil
|
||||||
}
|
}
|
||||||
|
@ -47,8 +47,10 @@ func (w *workflowInvoker) GetLogs() domain.WorkflowLogs {
|
|||||||
func (w *workflowInvoker) processNode(ctx context.Context, node *domain.WorkflowNode) error {
|
func (w *workflowInvoker) processNode(ctx context.Context, node *domain.WorkflowNode) error {
|
||||||
current := node
|
current := node
|
||||||
for current != nil {
|
for current != nil {
|
||||||
if ctx.Err() != nil {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if current.Type == domain.WorkflowNodeTypeBranch || current.Type == domain.WorkflowNodeTypeExecuteResultBranch {
|
if current.Type == domain.WorkflowNodeTypeBranch || current.Type == domain.WorkflowNodeTypeExecuteResultBranch {
|
||||||
|
@ -41,22 +41,25 @@ func (n *applyNode) Process(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检测是否可以跳过本次执行
|
// 检测是否可以跳过本次执行
|
||||||
if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable {
|
if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable {
|
||||||
n.logger.Info(fmt.Sprintf("skip this application, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("skip this application, because %s", reason))
|
||||||
return nil
|
return nil
|
||||||
} else if skipReason != "" {
|
} else if reason != "" {
|
||||||
n.logger.Info(fmt.Sprintf("re-apply, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("re-apply, because %s", reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化申请器
|
// 初始化申请器
|
||||||
applicant, err := applicant.NewWithApplyNode(n.node)
|
applicant, err := applicant.NewWithWorkflowNode(applicant.ApplicantWithWorkflowNodeConfig{
|
||||||
|
Node: n.node,
|
||||||
|
Logger: n.logger,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Warn("failed to create applicant provider")
|
n.logger.Warn("failed to create applicant provider")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 申请证书
|
// 申请证书
|
||||||
applyResult, err := applicant.Apply()
|
applyResult, err := applicant.Apply(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Warn("failed to apply")
|
n.logger.Warn("failed to apply")
|
||||||
return err
|
return err
|
||||||
|
@ -54,26 +54,27 @@ func (n *deployNode) Process(ctx context.Context) error {
|
|||||||
|
|
||||||
// 检测是否可以跳过本次执行
|
// 检测是否可以跳过本次执行
|
||||||
if lastOutput != nil && certificate.CreatedAt.Before(lastOutput.UpdatedAt) {
|
if lastOutput != nil && certificate.CreatedAt.Before(lastOutput.UpdatedAt) {
|
||||||
if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable {
|
if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable {
|
||||||
n.logger.Info(fmt.Sprintf("skip this deployment, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("skip this deployment, because %s", reason))
|
||||||
return nil
|
return nil
|
||||||
} else if skipReason != "" {
|
} else if reason != "" {
|
||||||
n.logger.Info(fmt.Sprintf("re-deploy, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("re-deploy, because %s", reason))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化部署器
|
// 初始化部署器
|
||||||
deployer, err := deployer.NewWithDeployNode(n.node, struct {
|
deployer, err := deployer.NewWithWorkflowNode(deployer.DeployerWithWorkflowNodeConfig{
|
||||||
Certificate string
|
Node: n.node,
|
||||||
PrivateKey string
|
Logger: n.logger,
|
||||||
}{Certificate: certificate.Certificate, PrivateKey: certificate.PrivateKey})
|
CertificatePEM: certificate.Certificate,
|
||||||
|
PrivateKeyPEM: certificate.PrivateKey,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Warn("failed to create deployer provider")
|
n.logger.Warn("failed to create deployer provider")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 部署证书
|
// 部署证书
|
||||||
deployer.SetLogger(n.logger)
|
|
||||||
if err := deployer.Deploy(ctx); err != nil {
|
if err := deployer.Deploy(ctx); err != nil {
|
||||||
n.logger.Warn("failed to deploy")
|
n.logger.Warn("failed to deploy")
|
||||||
return err
|
return err
|
||||||
|
@ -30,25 +30,50 @@ func (n *notifyNode) Process(ctx context.Context) error {
|
|||||||
|
|
||||||
nodeConfig := n.node.GetConfigForNotify()
|
nodeConfig := n.node.GetConfigForNotify()
|
||||||
|
|
||||||
// 获取通知配置
|
if nodeConfig.Provider == "" {
|
||||||
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
// Deprecated: v0.4.x 将废弃
|
||||||
|
// 兼容旧版本的通知渠道
|
||||||
|
n.logger.Warn("WARNING! you are using the notification channel from global settings, which will be deprecated in the future")
|
||||||
|
|
||||||
|
// 获取通知配置
|
||||||
|
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取通知渠道
|
||||||
|
channelConfig, err := settings.GetNotifyChannelConfig(nodeConfig.Channel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送通知
|
||||||
|
if err := notify.SendToChannel(nodeConfig.Subject, nodeConfig.Message, nodeConfig.Channel, channelConfig); err != nil {
|
||||||
|
n.logger.Warn("failed to notify", slog.String("channel", nodeConfig.Channel))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.logger.Info("notify completed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化通知器
|
||||||
|
deployer, err := notify.NewWithWorkflowNode(notify.NotifierWithWorkflowNodeConfig{
|
||||||
|
Node: n.node,
|
||||||
|
Logger: n.logger,
|
||||||
|
Subject: nodeConfig.Subject,
|
||||||
|
Message: nodeConfig.Message,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
n.logger.Warn("failed to create notifier provider")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取通知渠道
|
// 推送通知
|
||||||
channelConfig, err := settings.GetNotifyChannelConfig(nodeConfig.Channel)
|
if err := deployer.Notify(ctx); err != nil {
|
||||||
if err != nil {
|
n.logger.Warn("failed to notify")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
if err := notify.SendToChannel(nodeConfig.Subject, nodeConfig.Message, nodeConfig.Channel, channelConfig); err != nil {
|
|
||||||
n.logger.Warn("failed to notify", slog.String("channel", nodeConfig.Channel))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
n.logger.Info("notify completed")
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ func (n *uploadNode) Process(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检测是否可以跳过本次执行
|
// 检测是否可以跳过本次执行
|
||||||
if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable {
|
if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable {
|
||||||
n.logger.Info(fmt.Sprintf("skip this upload, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("skip this upload, because %s", reason))
|
||||||
return nil
|
return nil
|
||||||
} else if skipReason != "" {
|
} else if reason != "" {
|
||||||
n.logger.Info(fmt.Sprintf("re-upload, because %s", skipReason))
|
n.logger.Info(fmt.Sprintf("re-upload, because %s", reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成证书实体
|
// 生成证书实体
|
||||||
|
88
migrations/1745726400_upgrade.go
Normal file
88
migrations/1745726400_upgrade.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(app core.App) error {
|
||||||
|
// update collection `access`
|
||||||
|
{
|
||||||
|
collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text2859962647",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "reserve",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}`)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := app.Save(collection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrate data
|
||||||
|
{
|
||||||
|
accesses, err := app.FindAllRecords("access")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, access := range accesses {
|
||||||
|
changed := false
|
||||||
|
|
||||||
|
if access.GetString("provider") == "buypass" {
|
||||||
|
access.Set("reserve", "ca")
|
||||||
|
changed = true
|
||||||
|
} else if access.GetString("provider") == "googletrustservices" {
|
||||||
|
access.Set("reserve", "ca")
|
||||||
|
changed = true
|
||||||
|
} else if access.GetString("provider") == "sslcom" {
|
||||||
|
access.Set("reserve", "ca")
|
||||||
|
changed = true
|
||||||
|
} else if access.GetString("provider") == "zerossl" {
|
||||||
|
access.Set("reserve", "ca")
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if access.GetString("provider") == "webhook" {
|
||||||
|
config := make(map[string]any)
|
||||||
|
if err := access.UnmarshalJSONField("config", &config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
config["method"] = "POST"
|
||||||
|
config["headers"] = "Content-Type: application/json"
|
||||||
|
access.Set("config", config)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
err = app.Save(access)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, func(app core.App) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
1
ui/public/imgs/providers/dingtalk.svg
Normal file
1
ui/public/imgs/providers/dingtalk.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512.003 79C272.855 79 79 272.855 79 512.003 79 751.145 272.855 945 512.003 945 751.145 945 945 751.145 945 512.003 945 272.855 751.145 79 512.003 79z m200.075 375.014c-0.867 3.764-3.117 9.347-6.234 16.012h0.087l-0.347 0.648c-18.183 38.86-65.631 115.108-65.631 115.108l-0.215-0.52-13.856 24.147h66.8L565.063 779l29.002-115.368h-52.598l18.27-76.29c-14.76 3.55-32.253 8.436-52.945 15.1 0 0-27.967 16.36-80.607-31.5 0 0-35.501-31.29-14.891-39.078 8.744-3.33 42.466-7.573 69.004-11.122 35.93-4.845 57.965-7.441 57.965-7.441s-110.607 1.643-136.841-2.468c-26.237-4.11-59.525-47.905-66.626-86.377 0 0-10.953-21.117 23.595-11.122 34.547 10 177.535 38.95 177.535 38.95s-185.933-56.992-198.36-70.929c-12.381-13.846-36.406-75.902-33.289-113.981 0 0 1.343-9.521 11.127-6.926 0 0 137.49 62.75 231.475 97.152 94.028 34.403 175.76 51.885 165.2 96.414z" fill="#3AA2EB"></path></svg>
|
After Width: | Height: | Size: 1022 B |
1
ui/public/imgs/providers/email.svg
Normal file
1
ui/public/imgs/providers/email.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M832 128H192a128 128 0 0 0-128 128v512a128 128 0 0 0 128 128h384v-64H192a64 64 0 0 1-64-64V310.4l384 206.08 384-206.72V768h64V256a128 128 0 0 0-128-128zM512 443.52L131.2 240A64 64 0 0 1 192 192h640a64 64 0 0 1 60.8 46.72z" fill="#616971"></path><path d="M640 896h64v-64h-64z m256-64v64h64v-64z m-128 64h64v-64h-64z" fill="#FF8910"></path></svg>
|
After Width: | Height: | Size: 500 B |
1
ui/public/imgs/providers/lark.svg
Normal file
1
ui/public/imgs/providers/lark.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 24 KiB |
1
ui/public/imgs/providers/mattermost.svg
Normal file
1
ui/public/imgs/providers/mattermost.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M515.465911 0.01242C300.718451-1.427543 99.826634 133.353979 27.187509 347.877444c-90.654661 267.771091 52.898635 558.325595 320.665726 648.947257 267.771091 90.654661 558.325595-52.898635 648.947257-320.665726 73.698099-217.594386-7.327811-450.199385-183.932255-578.421077l5.375862 108.670197c88.097727 97.374488 122.87783 235.163933 79.006961 364.760588C831.811749 764.541694 615.912319 866.204071 415.1165 798.204826c-200.860818-67.966246-310.617986-279.83378-245.115676-473.23879 43.998865-129.949647 155.932977-218.494363 285.592631-241.915759L525.641648 0.268413A211.822535 211.822535 0 0 0 516.199892 0.044419h-0.79998zM665.996027 46.700215h-0.351991a12.193685 12.193685 0 0 0-4.959872 1.055973l0.063998-0.031999-0.159996 0.031999a14.076637 14.076637 0 0 0-4.63988 3.039921c-6.175841 6.047844-28.031277 35.327089-28.031277 35.327089L580.298238 145.097676l-55.454569 67.614256-95.231543 118.429944s-43.678873 54.526593-34.047122 121.629862c9.631751 67.166267 59.550464 99.838424 98.297464 112.990085 38.687002 13.087662 98.206466 17.407551 146.685215-30.047225C688.967434 488.258823 687.395475 418.372626 687.395475 418.372626l-3.711905-151.901081-2.975923-87.454744-2.011948-75.742046s0.415989-36.511058-0.863978-45.086836a13.914641 13.914641 0 0 0-1.53596-4.639881l0.031999 0.063999-0.255993-0.511987-0.287993-0.479988a11.878694 11.878694 0 0 0-9.759748-5.983845H665.996027z" fill="#0072C6"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
ui/public/imgs/providers/telegram.svg
Normal file
1
ui/public/imgs/providers/telegram.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M679.424 746.862l84.005-395.996c7.424-34.852-12.581-48.567-35.438-40.009L234.277 501.138c-33.72 13.13-33.134 32-5.706 40.558l126.282 39.424 293.156-184.576c13.714-9.143 26.295-3.986 16.018 5.157L426.898 615.973l-9.143 130.304c13.13 0 18.871-5.706 25.71-12.581l61.696-59.429 128 94.282c23.442 13.129 40.01 6.29 46.3-21.724zM1024 512c0 282.843-229.157 512-512 512S0 794.843 0 512 229.157 0 512 0s512 229.157 512 512z" fill="#1296DB"></path></svg>
|
1
ui/public/imgs/providers/wecom.svg
Normal file
1
ui/public/imgs/providers/wecom.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M803.6 859.2c0 26.6-20.4 49.2-48.4 51.8-21.2 2-48.6-11-54.8-39.8-5.6-26-13.2-50.8-29-72.4-6-8.2-12.8-16-20-23.4-7.6-8-9.4-14-4.4-19.8 5-5.8 12.8-5 20.8 3.2 20.8 21 45.4 35.2 73.6 43.6 7.2 2.2 14.8 3.4 22.2 5.2 24.6 6.2 40 26.2 40 51.6z" fill="#FC6401"></path><path d="M698.2 549.8c0.2-28.4 20.8-50.2 49.6-52.6 25.6-2.2 50.6 17.6 55 45.2 6 36.2 22.8 66.2 48.4 92 3.2 3.2 5.6 9.2 5.2 13.8-0.4 7.2-9.8 10.6-16.2 6.4-3.4-2.2-6.2-5-9-7.8-25.4-24.6-55.6-39.4-90.4-45.4-24.8-4.6-42.6-26.2-42.6-51.6z" fill="#2DBD00"></path><path d="M595.4 765.2c-26.6 0-49.2-20.4-51.8-48.4-2-21.2 11-48.6 39.8-54.8 26-5.6 50.8-13.2 72.4-29 8.2-6 16-12.8 23.4-20 8-7.6 14-9.4 19.8-4.4 5.8 5 5 12.8-3.2 20.8-21 20.8-35.2 45.4-43.6 73.6-2.2 7.2-3.4 14.8-5.2 22.2-6.2 24.6-26.2 40-51.6 40z" fill="#FFCD00"></path><path d="M898.8 650c28.4 0.2 50.2 20.8 52.6 49.6 2.2 25.6-17.6 50.6-45.2 55-36.2 6-66.2 22.8-92 48.4-3.2 3.2-9.2 5.6-13.8 5.2-7.2-0.4-10.6-9.8-6.4-16.2 2.2-3.4 5-6.2 7.8-9 24.6-25.4 39.4-55.6 45.4-90.4 4.6-25 26.2-42.8 51.6-42.6z" fill="#0084F0"></path><path d="M734 208.6c-110.4-108.4-244.8-139.8-392.4-100C81.6 178.8 1.2 449.2 132.6 625.6c6.4 8.6 7.6 24.4 5.2 35.4-7 32.4-17.4 64.2-26 96.2-4.6 17.2-7.4 34.6 8 48.4 16.6 14.8 34.2 11.8 52.2 2.6 29.6-15 59.8-29.2 89-45 19-10.4 36.2-10.8 57.6-4.8 42.8 11.8 87.2 18.4 109.6 23 43.8-0.8 83.6-5.2 120.2-13.6-13.8-12-23-29.2-24.8-49-0.4-5.4-0.2-10.8 0.6-16.2-57.8 12.4-119.2 7.4-183.2-14-42.2-14.2-76.8-17.8-113.4 7-3.4 2.2-7.8 2.8-24.6 8.2 33.8-58.4 8.8-95-19.6-136.6-63.4-92-50.4-210.8 24.6-296.4 122.8-139.8 363-139.8 485.8 0.2 52.8 60.2 73.6 135.2 61.8 206.2 28 1.6 52.8 20.6 63 47.2 32-108.8 4-228.8-84.6-315.8z" fill="#0083EF"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -14,14 +14,14 @@ export type AccessEditDrawerProps = {
|
|||||||
data?: AccessFormProps["initialValues"];
|
data?: AccessFormProps["initialValues"];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
range?: AccessFormProps["range"];
|
|
||||||
scene: AccessFormProps["scene"];
|
scene: AccessFormProps["scene"];
|
||||||
trigger?: React.ReactNode;
|
trigger?: React.ReactNode;
|
||||||
|
usage?: AccessFormProps["usage"];
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
afterSubmit?: (record: AccessModel) => void;
|
afterSubmit?: (record: AccessModel) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccessEditDrawer = ({ data, loading, trigger, scene, range, afterSubmit, ...props }: AccessEditDrawerProps) => {
|
const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditDrawerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [notificationApi, NotificationContextHolder] = notification.useNotification();
|
const [notificationApi, NotificationContextHolder] = notification.useNotification();
|
||||||
@ -109,7 +109,7 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, range, afterSubmit, .
|
|||||||
width={720}
|
width={720}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<AccessForm ref={formRef} initialValues={data} range={range} scene={scene === "add" ? "add" : "edit"} />
|
<AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -14,14 +14,14 @@ export type AccessEditModalProps = {
|
|||||||
data?: AccessFormProps["initialValues"];
|
data?: AccessFormProps["initialValues"];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
range?: AccessFormProps["range"];
|
usage?: AccessFormProps["usage"];
|
||||||
scene: AccessFormProps["scene"];
|
scene: AccessFormProps["scene"];
|
||||||
trigger?: React.ReactNode;
|
trigger?: React.ReactNode;
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
afterSubmit?: (record: AccessModel) => void;
|
afterSubmit?: (record: AccessModel) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccessEditModal = ({ data, loading, trigger, scene, range, afterSubmit, ...props }: AccessEditModalProps) => {
|
const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditModalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [notificationApi, NotificationContextHolder] = notification.useNotification();
|
const [notificationApi, NotificationContextHolder] = notification.useNotification();
|
||||||
@ -105,7 +105,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, range, afterSubmit, ..
|
|||||||
onCancel={handleCancelClick}
|
onCancel={handleCancelClick}
|
||||||
>
|
>
|
||||||
<div className="pb-2 pt-4">
|
<div className="pb-2 pt-4">
|
||||||
<AccessForm ref={formRef} initialValues={data} range={range} scene={scene === "add" ? "add" : "edit"} />
|
<AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
@ -25,10 +25,12 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
|
|||||||
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
||||||
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
||||||
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
|
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
|
||||||
|
import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig";
|
||||||
import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig";
|
import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig";
|
||||||
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
|
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
|
||||||
import AccessFormDynv6Config from "./AccessFormDynv6Config";
|
import AccessFormDynv6Config from "./AccessFormDynv6Config";
|
||||||
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
|
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
|
||||||
|
import AccessFormEmailConfig from "./AccessFormEmailConfig";
|
||||||
import AccessFormGcoreConfig from "./AccessFormGcoreConfig";
|
import AccessFormGcoreConfig from "./AccessFormGcoreConfig";
|
||||||
import AccessFormGnameConfig from "./AccessFormGnameConfig";
|
import AccessFormGnameConfig from "./AccessFormGnameConfig";
|
||||||
import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
|
import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
|
||||||
@ -36,6 +38,8 @@ import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServices
|
|||||||
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
|
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
|
||||||
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
|
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
|
||||||
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
|
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
|
||||||
|
import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig";
|
||||||
|
import AccessFormMattermostConfig from "./AccessFormMattermostConfig";
|
||||||
import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig";
|
import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig";
|
||||||
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
|
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
|
||||||
import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig";
|
import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig";
|
||||||
@ -47,6 +51,7 @@ import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
|
|||||||
import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
|
import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
|
||||||
import AccessFormSSHConfig from "./AccessFormSSHConfig";
|
import AccessFormSSHConfig from "./AccessFormSSHConfig";
|
||||||
import AccessFormSSLComConfig from "./AccessFormSSLComConfig";
|
import AccessFormSSLComConfig from "./AccessFormSSLComConfig";
|
||||||
|
import AccessFormTelegramConfig from "./AccessFormTelegramConfig";
|
||||||
import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
|
import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
|
||||||
import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
|
import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
|
||||||
import AccessFormUpyunConfig from "./AccessFormUpyunConfig";
|
import AccessFormUpyunConfig from "./AccessFormUpyunConfig";
|
||||||
@ -54,20 +59,21 @@ import AccessFormVercelConfig from "./AccessFormVercelConfig";
|
|||||||
import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig";
|
import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig";
|
||||||
import AccessFormWangsuConfig from "./AccessFormWangsuConfig";
|
import AccessFormWangsuConfig from "./AccessFormWangsuConfig";
|
||||||
import AccessFormWebhookConfig from "./AccessFormWebhookConfig";
|
import AccessFormWebhookConfig from "./AccessFormWebhookConfig";
|
||||||
|
import AccessFormWeComBotConfig from "./AccessFormWeComBotConfig";
|
||||||
import AccessFormWestcnConfig from "./AccessFormWestcnConfig";
|
import AccessFormWestcnConfig from "./AccessFormWestcnConfig";
|
||||||
import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig";
|
import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig";
|
||||||
|
|
||||||
type AccessFormFieldValues = Partial<MaybeModelRecord<AccessModel>>;
|
type AccessFormFieldValues = Partial<MaybeModelRecord<AccessModel>>;
|
||||||
type AccessFormRanges = "both-dns-hosting" | "ca-only" | "notify-only";
|
|
||||||
type AccessFormScenes = "add" | "edit";
|
type AccessFormScenes = "add" | "edit";
|
||||||
|
type AccessFormUsages = "both-dns-hosting" | "ca-only" | "notification-only";
|
||||||
|
|
||||||
export type AccessFormProps = {
|
export type AccessFormProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
initialValues?: AccessFormFieldValues;
|
initialValues?: AccessFormFieldValues;
|
||||||
range?: AccessFormRanges;
|
|
||||||
scene: AccessFormScenes;
|
scene: AccessFormScenes;
|
||||||
|
usage?: AccessFormUsages;
|
||||||
onValuesChange?: (values: AccessFormFieldValues) => void;
|
onValuesChange?: (values: AccessFormFieldValues) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,7 +83,7 @@ export type AccessFormInstance = {
|
|||||||
validateFields: FormInstance<AccessFormFieldValues>["validateFields"];
|
validateFields: FormInstance<AccessFormFieldValues>["validateFields"];
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, style, disabled, initialValues, range, scene, onValuesChange }, ref) => {
|
const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, style, disabled, initialValues, usage, scene, onValuesChange }, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
@ -88,13 +94,14 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
.trim(),
|
.trim(),
|
||||||
provider: z.nativeEnum(ACCESS_PROVIDERS, {
|
provider: z.nativeEnum(ACCESS_PROVIDERS, {
|
||||||
message:
|
message:
|
||||||
range === "ca-only"
|
usage === "ca-only"
|
||||||
? t("access.form.certificate_authority.placeholder")
|
? t("access.form.certificate_authority.placeholder")
|
||||||
: range === "notify-only"
|
: usage === "notification-only"
|
||||||
? t("access.form.notification_channel.placeholder")
|
? t("access.form.notification_channel.placeholder")
|
||||||
: t("access.form.provider.placeholder"),
|
: t("access.form.provider.placeholder"),
|
||||||
}),
|
}),
|
||||||
config: z.any(),
|
config: z.any(),
|
||||||
|
reserve: z.string().nullish(),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
const { form: formInst, formProps } = useAntdForm({
|
const { form: formInst, formProps } = useAntdForm({
|
||||||
@ -102,33 +109,33 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
});
|
});
|
||||||
|
|
||||||
const providerLabel = useMemo(() => {
|
const providerLabel = useMemo(() => {
|
||||||
switch (range) {
|
switch (usage) {
|
||||||
case "ca-only":
|
case "ca-only":
|
||||||
return t("access.form.certificate_authority.label");
|
return t("access.form.certificate_authority.label");
|
||||||
case "notify-only":
|
case "notification-only":
|
||||||
return t("access.form.notification_channel.label");
|
return t("access.form.notification_channel.label");
|
||||||
}
|
}
|
||||||
|
|
||||||
return t("access.form.provider.label");
|
return t("access.form.provider.label");
|
||||||
}, [range]);
|
}, [usage]);
|
||||||
const providerPlaceholder = useMemo(() => {
|
const providerPlaceholder = useMemo(() => {
|
||||||
switch (range) {
|
switch (usage) {
|
||||||
case "ca-only":
|
case "ca-only":
|
||||||
return t("access.form.certificate_authority.placeholder");
|
return t("access.form.certificate_authority.placeholder");
|
||||||
case "notify-only":
|
case "notification-only":
|
||||||
return t("access.form.notification_channel.placeholder");
|
return t("access.form.notification_channel.placeholder");
|
||||||
}
|
}
|
||||||
|
|
||||||
return t("access.form.provider.placeholder");
|
return t("access.form.provider.placeholder");
|
||||||
}, [range]);
|
}, [usage]);
|
||||||
const providerTooltip = useMemo(() => {
|
const providerTooltip = useMemo(() => {
|
||||||
switch (range) {
|
switch (usage) {
|
||||||
case "both-dns-hosting":
|
case "both-dns-hosting":
|
||||||
return <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.tooltip") }}></span>;
|
return <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.tooltip") }}></span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [range]);
|
}, [usage]);
|
||||||
|
|
||||||
const fieldProvider = Form.useWatch("provider", formInst);
|
const fieldProvider = Form.useWatch("provider", formInst);
|
||||||
|
|
||||||
@ -179,6 +186,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.DESEC:
|
case ACCESS_PROVIDERS.DESEC:
|
||||||
return <AccessFormDeSECConfig {...nestedFormProps} />;
|
return <AccessFormDeSECConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.DINGTALKBOT:
|
||||||
|
return <AccessFormDingTalkBotConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.DNSLA:
|
case ACCESS_PROVIDERS.DNSLA:
|
||||||
return <AccessFormDNSLAConfig {...nestedFormProps} />;
|
return <AccessFormDNSLAConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.DOGECLOUD:
|
case ACCESS_PROVIDERS.DOGECLOUD:
|
||||||
@ -195,12 +204,18 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
return <AccessFormGoogleTrustServicesConfig {...nestedFormProps} />;
|
return <AccessFormGoogleTrustServicesConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.EDGIO:
|
case ACCESS_PROVIDERS.EDGIO:
|
||||||
return <AccessFormEdgioConfig {...nestedFormProps} />;
|
return <AccessFormEdgioConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.EMAIL:
|
||||||
|
return <AccessFormEmailConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.HUAWEICLOUD:
|
case ACCESS_PROVIDERS.HUAWEICLOUD:
|
||||||
return <AccessFormHuaweiCloudConfig {...nestedFormProps} />;
|
return <AccessFormHuaweiCloudConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.JDCLOUD:
|
case ACCESS_PROVIDERS.JDCLOUD:
|
||||||
return <AccessFormJDCloudConfig {...nestedFormProps} />;
|
return <AccessFormJDCloudConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.KUBERNETES:
|
case ACCESS_PROVIDERS.KUBERNETES:
|
||||||
return <AccessFormKubernetesConfig {...nestedFormProps} />;
|
return <AccessFormKubernetesConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.LARKBOT:
|
||||||
|
return <AccessFormLarkBotConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.MATTERMOST:
|
||||||
|
return <AccessFormMattermostConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.NAMECHEAP:
|
case ACCESS_PROVIDERS.NAMECHEAP:
|
||||||
return <AccessFormNamecheapConfig {...nestedFormProps} />;
|
return <AccessFormNamecheapConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.NAMEDOTCOM:
|
case ACCESS_PROVIDERS.NAMEDOTCOM:
|
||||||
@ -221,6 +236,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
return <AccessFormSafeLineConfig {...nestedFormProps} />;
|
return <AccessFormSafeLineConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.SSH:
|
case ACCESS_PROVIDERS.SSH:
|
||||||
return <AccessFormSSHConfig {...nestedFormProps} />;
|
return <AccessFormSSHConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.TELEGRAM:
|
||||||
|
return <AccessFormTelegramConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.SSLCOM:
|
case ACCESS_PROVIDERS.SSLCOM:
|
||||||
return <AccessFormSSLComConfig {...nestedFormProps} />;
|
return <AccessFormSSLComConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.TENCENTCLOUD:
|
case ACCESS_PROVIDERS.TENCENTCLOUD:
|
||||||
@ -236,7 +253,14 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
case ACCESS_PROVIDERS.WANGSU:
|
case ACCESS_PROVIDERS.WANGSU:
|
||||||
return <AccessFormWangsuConfig {...nestedFormProps} />;
|
return <AccessFormWangsuConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.WEBHOOK:
|
case ACCESS_PROVIDERS.WEBHOOK:
|
||||||
return <AccessFormWebhookConfig {...nestedFormProps} />;
|
return (
|
||||||
|
<AccessFormWebhookConfig
|
||||||
|
usage={usage === "notification-only" ? "notification" : usage === "both-dns-hosting" ? "deployment" : "none"}
|
||||||
|
{...nestedFormProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case ACCESS_PROVIDERS.WECOMBOT:
|
||||||
|
return <AccessFormWeComBotConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.WESTCN:
|
case ACCESS_PROVIDERS.WESTCN:
|
||||||
return <AccessFormWestcnConfig {...nestedFormProps} />;
|
return <AccessFormWestcnConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.ZEROSSL:
|
case ACCESS_PROVIDERS.ZEROSSL:
|
||||||
@ -260,6 +284,7 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
getFieldsValue: () => {
|
getFieldsValue: () => {
|
||||||
const values = formInst.getFieldsValue(true);
|
const values = formInst.getFieldsValue(true);
|
||||||
values.config = nestedFormInst.getFieldsValue();
|
values.config = nestedFormInst.getFieldsValue();
|
||||||
|
values.reserve = usage === "ca-only" ? "ca" : usage === "notification-only" ? "notification" : undefined;
|
||||||
return values;
|
return values;
|
||||||
},
|
},
|
||||||
resetFields: (fields) => {
|
resetFields: (fields) => {
|
||||||
@ -288,20 +313,20 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
<Form.Item name="provider" label={providerLabel} rules={[formRule]} tooltip={providerTooltip}>
|
<Form.Item name="provider" label={providerLabel} rules={[formRule]} tooltip={providerTooltip}>
|
||||||
<AccessProviderSelect
|
<AccessProviderSelect
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
if (range == null) return true;
|
if (usage == null) return true;
|
||||||
|
|
||||||
switch (range) {
|
switch (usage) {
|
||||||
case "both-dns-hosting":
|
case "both-dns-hosting":
|
||||||
return record.usages.includes(ACCESS_USAGES.DNS) || record.usages.includes(ACCESS_USAGES.HOSTING);
|
return record.usages.includes(ACCESS_USAGES.DNS) || record.usages.includes(ACCESS_USAGES.HOSTING);
|
||||||
case "ca-only":
|
case "ca-only":
|
||||||
return record.usages.includes(ACCESS_USAGES.CA);
|
return record.usages.includes(ACCESS_USAGES.CA);
|
||||||
case "notify-only":
|
case "notification-only":
|
||||||
return record.usages.includes(ACCESS_USAGES.NOTIFICATION);
|
return record.usages.includes(ACCESS_USAGES.NOTIFICATION);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={scene !== "add"}
|
disabled={scene !== "add"}
|
||||||
placeholder={providerPlaceholder}
|
placeholder={providerPlaceholder}
|
||||||
showOptionTags={range == null || (range === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)}
|
showOptionTags={usage == null || (usage === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)}
|
||||||
showSearch={!disabled}
|
showSearch={!disabled}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
68
ui/src/components/access/AccessFormDingTalkBotConfig.tsx
Normal file
68
ui/src/components/access/AccessFormDingTalkBotConfig.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForDingTalkBot } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormDingTalkBotConfigFieldValues = Nullish<AccessConfigForDingTalkBot>;
|
||||||
|
|
||||||
|
export type AccessFormDingTalkBotConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormDingTalkBotConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormDingTalkBotConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormDingTalkBotConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
webhookUrl: "",
|
||||||
|
secret: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormDingTalkBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDingTalkBotConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
webhookUrl: z.string().url(t("common.errmsg.url_invalid")),
|
||||||
|
secret: z.string().nonempty(t("access.form.dingtalkbot_secret.placeholder")).trim(),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="webhookUrl"
|
||||||
|
label={t("access.form.dingtalkbot_webhook_url.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dingtalkbot_webhook_url.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("access.form.dingtalkbot_webhook_url.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="secret"
|
||||||
|
label={t("access.form.dingtalkbot_secret.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dingtalkbot_secret.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input.Password autoComplete="new-password" placeholder={t("access.form.dingtalkbot_secret.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormDingTalkBotConfig;
|
125
ui/src/components/access/AccessFormEmailConfig.tsx
Normal file
125
ui/src/components/access/AccessFormEmailConfig.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input, InputNumber, Switch } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForEmail } from "@/domain/access";
|
||||||
|
import { validEmailAddress, validPortNumber } from "@/utils/validators";
|
||||||
|
|
||||||
|
type AccessFormEmailConfigFieldValues = Nullish<AccessConfigForEmail>;
|
||||||
|
|
||||||
|
export type AccessFormEmailConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormEmailConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormEmailConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormEmailConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
smtpHost: "",
|
||||||
|
smtpPort: 465,
|
||||||
|
smtpTls: true,
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormEmailConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
smtpHost: z
|
||||||
|
.string()
|
||||||
|
.min(1, t("access.form.email_smtp_host.placeholder"))
|
||||||
|
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||||
|
smtpPort: z.preprocess(
|
||||||
|
(v) => Number(v),
|
||||||
|
z.number().refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
|
),
|
||||||
|
smtpTls: z.boolean().nullish(),
|
||||||
|
username: z
|
||||||
|
.string()
|
||||||
|
.min(1, t("access.form.email_username.placeholder"))
|
||||||
|
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1, t("access.form.email_password.placeholder"))
|
||||||
|
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||||
|
defaultSenderAddress: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (!v) return true;
|
||||||
|
return validEmailAddress(v);
|
||||||
|
}, t("common.errmsg.email_invalid")),
|
||||||
|
defaultReceiverAddress: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (!v) return true;
|
||||||
|
return validEmailAddress(v);
|
||||||
|
}, t("common.errmsg.email_invalid")),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleTlsSwitchChange = (checked: boolean) => {
|
||||||
|
const oldPort = formInst.getFieldValue("smtpPort");
|
||||||
|
const newPort = checked && (oldPort == null || oldPort === 25) ? 465 : !checked && (oldPort == null || oldPort === 465) ? 25 : oldPort;
|
||||||
|
if (newPort !== oldPort) {
|
||||||
|
formInst.setFieldValue("smtpPort", newPort);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<div className="w-3/5">
|
||||||
|
<Form.Item name="smtpHost" label={t("access.form.email_smtp_host.label")} rules={[formRule]}>
|
||||||
|
<Input placeholder={t("access.form.email_smtp_host.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-2/5">
|
||||||
|
<Form.Item name="smtpPort" label={t("access.form.email_smtp_port.label")} rules={[formRule]}>
|
||||||
|
<InputNumber className="w-full" placeholder={t("access.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Form.Item name="smtpTls" label={t("access.form.email_smtp_tls.label")} rules={[formRule]}>
|
||||||
|
<Switch onChange={handleTlsSwitchChange} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="username" label={t("access.form.email_username.label")} rules={[formRule]}>
|
||||||
|
<Input autoComplete="new-password" placeholder={t("access.form.email_username.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="password" label={t("access.form.email_password.label")} rules={[formRule]}>
|
||||||
|
<Input.Password autoComplete="new-password" placeholder={t("access.form.email_password.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="defaultSenderAddress" label={t("access.form.email_default_sender_address.label")} rules={[formRule]}>
|
||||||
|
<Input type="email" allowClear placeholder={t("access.form.email_default_sender_address.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="defaultReceiverAddress" label={t("access.form.email_default_receiver_address.label")} rules={[formRule]}>
|
||||||
|
<Input type="email" allowClear placeholder={t("access.form.email_default_receiver_address.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormEmailConfig;
|
57
ui/src/components/access/AccessFormLarkBotConfig.tsx
Normal file
57
ui/src/components/access/AccessFormLarkBotConfig.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForLarkBot } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormLarkBotConfigFieldValues = Nullish<AccessConfigForLarkBot>;
|
||||||
|
|
||||||
|
export type AccessFormLarkBotConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormLarkBotConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormLarkBotConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormLarkBotConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
webhookUrl: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormLarkBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormLarkBotConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
webhookUrl: z.string().url(t("common.errmsg.url_invalid")),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="webhookUrl"
|
||||||
|
label={t("access.form.larkbot_webhook_url.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.larkbot_webhook_url.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("access.form.larkbot_webhook_url.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormLarkBotConfig;
|
79
ui/src/components/access/AccessFormMattermostConfig.tsx
Normal file
79
ui/src/components/access/AccessFormMattermostConfig.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForMattermost } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormMattermostConfigFieldValues = Nullish<AccessConfigForMattermost>;
|
||||||
|
|
||||||
|
export type AccessFormMattermostConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormMattermostConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormMattermostConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormMattermostConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
serverUrl: "http://<your-host-addr>:8065/",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormMattermostConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormMattermostConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
serverUrl: z.string().url(t("common.errmsg.url_invalid")),
|
||||||
|
username: z.string().nonempty(t("access.form.mattermost_username.placeholder")),
|
||||||
|
password: z.string().nonempty(t("access.form.mattermost_password.placeholder")),
|
||||||
|
defaultChannelId: z.string().nullish(),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="serverUrl"
|
||||||
|
label={t("access.form.mattermost_server_url.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.mattermost_server_url.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("access.form.mattermost_server_url.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="username" label={t("access.form.mattermost_username.label")} rules={[formRule]}>
|
||||||
|
<Input placeholder={t("access.form.mattermost_username.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="password" label={t("access.form.mattermost_password.label")} rules={[formRule]}>
|
||||||
|
<Input.Password placeholder={t("access.form.mattermost_password.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="defaultChannelId"
|
||||||
|
label={t("access.form.mattermost_default_channel_id.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.mattermost_default_channel_id.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input allowClear placeholder={t("access.form.mattermost_default_channel_id.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormMattermostConfig;
|
@ -31,13 +31,11 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
host: z
|
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||||
.string({ message: t("access.form.ssh_host.placeholder") })
|
|
||||||
.refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
|
||||||
port: z.preprocess(
|
port: z.preprocess(
|
||||||
(v) => Number(v),
|
(v) => Number(v),
|
||||||
z
|
z
|
||||||
.number({ message: t("access.form.ssh_port.placeholder") })
|
.number()
|
||||||
.int(t("access.form.ssh_port.placeholder"))
|
.int(t("access.form.ssh_port.placeholder"))
|
||||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
),
|
),
|
||||||
|
81
ui/src/components/access/AccessFormTelegramConfig.tsx
Normal file
81
ui/src/components/access/AccessFormTelegramConfig.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForTelegram } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormTelegramConfigFieldValues = Nullish<AccessConfigForTelegram>;
|
||||||
|
|
||||||
|
export type AccessFormTelegramConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormTelegramConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormTelegramConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormTelegramConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
botToken: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormTelegramConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
botToken: z
|
||||||
|
.string({ message: t("access.form.telegram_bot_token.placeholder") })
|
||||||
|
.min(1, t("access.form.telegram_bot_token.placeholder"))
|
||||||
|
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||||
|
defaultChatId: z
|
||||||
|
.preprocess(
|
||||||
|
(v) => (v == null || v === "" ? undefined : Number(v)),
|
||||||
|
z
|
||||||
|
.number()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (v == null || v + "" === "") return true;
|
||||||
|
return /^\d+$/.test(v + "") && +v! > 0;
|
||||||
|
}, t("access.form.telegram_default_chat_id.placeholder"))
|
||||||
|
)
|
||||||
|
.nullish(),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="botToken"
|
||||||
|
label={t("access.form.telegram_bot_token.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegram_bot_token.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input.Password autoComplete="new-password" placeholder={t("access.form.telegram_bot_token.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="defaultChatId"
|
||||||
|
label={t("access.form.telegram_default_chat_id.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.telegram_default_chat_id.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input type="number" allowClear placeholder={t("access.form.telegram_default_chat_id.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormTelegramConfig;
|
57
ui/src/components/access/AccessFormWeComBotConfig.tsx
Normal file
57
ui/src/components/access/AccessFormWeComBotConfig.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { type AccessConfigForWeComBot } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormWeComBotConfigFieldValues = Nullish<AccessConfigForWeComBot>;
|
||||||
|
|
||||||
|
export type AccessFormWeComBotConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormWeComBotConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormWeComBotConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormWeComBotConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
webhookUrl: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormWeComBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormWeComBotConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
webhookUrl: z.string().url(t("common.errmsg.url_invalid")),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="webhookUrl"
|
||||||
|
label={t("access.form.wecombot_webhook_url.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.wecombot_webhook_url.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("access.form.wecombot_webhook_url.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormWeComBotConfig;
|
@ -1,8 +1,10 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Form, type FormInstance, Input, Switch } from "antd";
|
import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons";
|
||||||
|
import { Alert, Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from "antd";
|
||||||
import { createSchemaFieldRule } from "antd-zod";
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import Show from "@/components/Show";
|
||||||
import { type AccessConfigForWebhook } from "@/domain/access";
|
import { type AccessConfigForWebhook } from "@/domain/access";
|
||||||
|
|
||||||
type AccessFormWebhookConfigFieldValues = Nullish<AccessConfigForWebhook>;
|
type AccessFormWebhookConfigFieldValues = Nullish<AccessConfigForWebhook>;
|
||||||
@ -12,24 +14,241 @@ export type AccessFormWebhookConfigProps = {
|
|||||||
formName: string;
|
formName: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
initialValues?: AccessFormWebhookConfigFieldValues;
|
initialValues?: AccessFormWebhookConfigFieldValues;
|
||||||
|
usage?: "deployment" | "notification" | "none";
|
||||||
onValuesChange?: (values: AccessFormWebhookConfigFieldValues) => void;
|
onValuesChange?: (values: AccessFormWebhookConfigFieldValues) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initFormModel = (): AccessFormWebhookConfigFieldValues => {
|
const initFormModel = (): AccessFormWebhookConfigFieldValues => {
|
||||||
return {
|
return {
|
||||||
url: "",
|
url: "",
|
||||||
|
method: "POST",
|
||||||
|
headers: "Content-Type: application/json",
|
||||||
|
allowInsecureConnections: false,
|
||||||
|
defaultDataForDeployment: JSON.stringify(
|
||||||
|
{
|
||||||
|
name: "${DOMAINS}",
|
||||||
|
cert: "${CERTIFICATE}",
|
||||||
|
privkey: "${PRIVATE_KEY}",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
),
|
||||||
|
defaultDataForNotification: JSON.stringify(
|
||||||
|
{
|
||||||
|
subject: "${SUBJECT}",
|
||||||
|
message: "${MESSAGE}",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormWebhookConfigProps) => {
|
const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialValues, usage, onValuesChange }: AccessFormWebhookConfigProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
url: z.string({ message: t("access.form.webhook_url.placeholder") }).url(t("common.errmsg.url_invalid")),
|
url: z.string().url(t("common.errmsg.url_invalid")),
|
||||||
|
method: z.union([z.literal("GET"), z.literal("POST"), z.literal("PUT"), z.literal("PATCH"), z.literal("DELETE")], {
|
||||||
|
message: t("access.form.webhook_method.placeholder"),
|
||||||
|
}),
|
||||||
|
headers: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (!v) return true;
|
||||||
|
|
||||||
|
const lines = v.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.split(":").length < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, t("access.form.webhook_headers.errmsg.invalid")),
|
||||||
allowInsecureConnections: z.boolean().nullish(),
|
allowInsecureConnections: z.boolean().nullish(),
|
||||||
|
defaultDataForDeployment: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (usage && usage !== "deployment") return true;
|
||||||
|
if (!v) return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(v);
|
||||||
|
return typeof obj === "object" && !Array.isArray(obj);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, t("access.form.webhook_default_data.errmsg.json_invalid")),
|
||||||
|
defaultDataForNotification: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => {
|
||||||
|
if (usage && usage !== "notification") return true;
|
||||||
|
if (!v) return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(v);
|
||||||
|
return typeof obj === "object" && !Array.isArray(obj);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, t("access.form.webhook_default_data.errmsg.json_invalid")),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleWebhookHeadersBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||||
|
let value = e.target.value;
|
||||||
|
value = value.trim();
|
||||||
|
value = value.replace(/(?<!\r)\n/g, "\r\n");
|
||||||
|
formInst.setFieldValue("headers", value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWebhookDataForDeploymentBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
try {
|
||||||
|
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||||
|
formInst.setFieldValue("defaultDataForDeployment", json);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWebhookDataForNotificationBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
try {
|
||||||
|
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||||
|
formInst.setFieldValue("defaultDataForNotification", json);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePresetDataForDeploymentClick = () => {
|
||||||
|
formInst.setFieldValue("defaultDataForDeployment", initFormModel().defaultDataForDeployment);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePresetDataForNotificationClick = (key: string) => {
|
||||||
|
switch (key) {
|
||||||
|
case "bark":
|
||||||
|
formInst.setFieldValue("url", "https://api.day.app/push");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json\r\nAuthorization: Bearer <your-gotify-token>");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
title: "${SUBJECT}",
|
||||||
|
body: "${MESSAGE}",
|
||||||
|
group: "<your-bark-group>",
|
||||||
|
device_keys: "<your-bark-device-key>",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "gotify":
|
||||||
|
formInst.setFieldValue("url", "https://<your-gotify-server>/");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json\r\nAuthorization: Bearer <your-gotify-token>");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
title: "${SUBJECT}",
|
||||||
|
message: "${MESSAGE}",
|
||||||
|
priority: 1,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ntfy":
|
||||||
|
formInst.setFieldValue("url", "https://<your-ntfy-server>/");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
topic: "<your-ntfy-topic>",
|
||||||
|
title: "${SUBJECT}",
|
||||||
|
message: "${MESSAGE}",
|
||||||
|
priority: 1,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pushover":
|
||||||
|
formInst.setFieldValue("url", "https://api.pushover.net/1/messages.json");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
token: "<your-pushover-token>",
|
||||||
|
user: "<your-pushover-user>",
|
||||||
|
title: "${SUBJECT}",
|
||||||
|
message: "${MESSAGE}",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pushplus":
|
||||||
|
formInst.setFieldValue("url", "https://www.pushplus.plus/send");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
token: "<your-pushplus-token>",
|
||||||
|
title: "${SUBJECT}",
|
||||||
|
content: "${MESSAGE}",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "serverchan":
|
||||||
|
formInst.setFieldValue("url", "https://sctapi.ftqq.com/<your-serverchan-key>.send");
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json");
|
||||||
|
formInst.setFieldValue(
|
||||||
|
"defaultDataForNotification",
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
text: "${SUBJECT}",
|
||||||
|
desp: "${MESSAGE}",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
formInst.setFieldValue("method", "POST");
|
||||||
|
formInst.setFieldValue("headers", "Content-Type: application/json");
|
||||||
|
formInst.setFieldValue("defaultDataForNotification", initFormModel().defaultDataForNotification);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
onValuesChange?.(values);
|
onValuesChange?.(values);
|
||||||
};
|
};
|
||||||
@ -47,6 +266,92 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
|||||||
<Input placeholder={t("access.form.webhook_url.placeholder")} />
|
<Input placeholder={t("access.form.webhook_url.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="method" label={t("access.form.webhook_method.label")} rules={[formRule]}>
|
||||||
|
<Select
|
||||||
|
options={["GET", "POST", "PUT", "PATCH", "DELETE"].map((s) => ({ label: s, value: s }))}
|
||||||
|
placeholder={t("access.form.webhook_method.placeholder")}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="headers"
|
||||||
|
label={t("access.form.webhook_headers.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_headers.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input.TextArea autoSize={{ minRows: 3, maxRows: 5 }} placeholder={t("access.form.webhook_headers.placeholder")} onBlur={handleWebhookHeadersBlur} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Show when={!usage || usage === "deployment"}>
|
||||||
|
<Form.Item className="mb-0">
|
||||||
|
<label className="mb-1 block">
|
||||||
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
|
<div className="max-w-full grow truncate">
|
||||||
|
<span>{t("access.form.webhook_default_data_for_deployment.label")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<Button size="small" type="link" onClick={handlePresetDataForDeploymentClick}>
|
||||||
|
{t("access.form.webhook_preset_data.button")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<Form.Item name="defaultDataForDeployment" rules={[formRule]}>
|
||||||
|
<Input.TextArea
|
||||||
|
allowClear
|
||||||
|
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||||
|
placeholder={t("access.form.webhook_default_data_for_deployment.placeholder")}
|
||||||
|
onBlur={handleWebhookDataForDeploymentBlur}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_default_data_for_deployment.guide") }}></span>} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={!usage || usage === "notification"}>
|
||||||
|
<Form.Item className="mb-0">
|
||||||
|
<label className="mb-1 block">
|
||||||
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
|
<div className="max-w-full grow truncate">
|
||||||
|
<span>{t("access.form.webhook_default_data_for_notification.label")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<Dropdown
|
||||||
|
menu={{
|
||||||
|
items: ["bark", "ntfy", "gotify", "pushover", "pushplus", "serverchan", "common"].map((key) => ({
|
||||||
|
key,
|
||||||
|
label: t(`access.form.webhook_preset_data.option.${key}.label`),
|
||||||
|
onClick: () => handlePresetDataForNotificationClick(key),
|
||||||
|
})),
|
||||||
|
}}
|
||||||
|
trigger={["click"]}
|
||||||
|
>
|
||||||
|
<Button size="small" type="link">
|
||||||
|
{t("access.form.webhook_preset_data.button")}
|
||||||
|
<DownOutlinedIcon />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<Form.Item name="defaultDataForNotification" rules={[formRule]}>
|
||||||
|
<Input.TextArea
|
||||||
|
allowClear
|
||||||
|
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||||
|
placeholder={t("access.form.webhook_default_data_for_notification.placeholder")}
|
||||||
|
onBlur={handleWebhookDataForNotificationBlur}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_default_data_for_notification.guide") }}></span>} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="allowInsecureConnections"
|
name="allowInsecureConnections"
|
||||||
label={t("access.form.webhook_allow_insecure_conns.label")}
|
label={t("access.form.webhook_allow_insecure_conns.label")}
|
||||||
|
@ -34,6 +34,9 @@ export type NotifyChannelEditFormInstance = {
|
|||||||
validateFields: FormInstance<NotifyChannelEditFormFieldValues>["validateFields"];
|
validateFields: FormInstance<NotifyChannelEditFormFieldValues>["validateFields"];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
const NotifyChannelEditForm = forwardRef<NotifyChannelEditFormInstance, NotifyChannelEditFormProps>(
|
const NotifyChannelEditForm = forwardRef<NotifyChannelEditFormInstance, NotifyChannelEditFormProps>(
|
||||||
({ className, style, channel, disabled, initialValues, onValuesChange }, ref) => {
|
({ className, style, channel, disabled, initialValues, onValuesChange }, ref) => {
|
||||||
const { form: formInst, formProps } = useAntdForm({
|
const { form: formInst, formProps } = useAntdForm({
|
||||||
|
@ -7,9 +7,7 @@ const NotifyChannelEditFormMattermostFields = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
serverUrl: z
|
serverUrl: z.string({ message: t("settings.notification.channel.form.mattermost_server_url.placeholder") }).url(t("common.errmsg.url_invalid")),
|
||||||
.string({ message: t("settings.notification.channel.form.mattermost_server_url.placeholder") })
|
|
||||||
.url(t("common.errmsg.url_invalid")),
|
|
||||||
channelId: z
|
channelId: z
|
||||||
.string({ message: t("settings.notification.channel.form.mattermost_channel_id.placeholder") })
|
.string({ message: t("settings.notification.channel.form.mattermost_channel_id.placeholder") })
|
||||||
.nonempty(t("settings.notification.channel.form.mattermost_channel_id.placeholder")),
|
.nonempty(t("settings.notification.channel.form.mattermost_channel_id.placeholder")),
|
||||||
@ -42,19 +40,11 @@ const NotifyChannelEditFormMattermostFields = () => {
|
|||||||
<Input placeholder={t("settings.notification.channel.form.mattermost_channel_id.placeholder")} />
|
<Input placeholder={t("settings.notification.channel.form.mattermost_channel_id.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item name="username" label={t("settings.notification.channel.form.mattermost_username.label")} rules={[formRule]}>
|
||||||
name="username"
|
|
||||||
label={t("settings.notification.channel.form.mattermost_username.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
>
|
|
||||||
<Input placeholder={t("settings.notification.channel.form.mattermost_username.placeholder")} />
|
<Input placeholder={t("settings.notification.channel.form.mattermost_username.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item name="password" label={t("settings.notification.channel.form.mattermost_password.label")} rules={[formRule]}>
|
||||||
name="password"
|
|
||||||
label={t("settings.notification.channel.form.mattermost_password.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
>
|
|
||||||
<Input.Password placeholder={t("settings.notification.channel.form.mattermost_password.placeholder")} />
|
<Input.Password placeholder={t("settings.notification.channel.form.mattermost_password.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
|
@ -3,9 +3,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Typography } from "antd";
|
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Typography } from "antd";
|
||||||
|
|
||||||
import Show from "@/components/Show";
|
import Show from "@/components/Show";
|
||||||
import { applyDNSProvidersMap } from "@/domain/provider";
|
import { acmeDns01ProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type ApplyDNSProviderPickerProps = {
|
export type ACMEDns01ProviderPickerProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
@ -13,7 +13,7 @@ export type ApplyDNSProviderPickerProps = {
|
|||||||
onSelect?: (value: string) => void;
|
onSelect?: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: ApplyDNSProviderPickerProps) => {
|
const ACMEDns01ProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: ACMEDns01ProviderPickerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [keyword, setKeyword] = useState<string>();
|
const [keyword, setKeyword] = useState<string>();
|
||||||
@ -25,7 +25,7 @@ const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSe
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const providers = useMemo(() => {
|
const providers = useMemo(() => {
|
||||||
return Array.from(applyDNSProvidersMap.values()).filter((provider) => {
|
return Array.from(acmeDns01ProvidersMap.values()).filter((provider) => {
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
const value = keyword.toLowerCase();
|
const value = keyword.toLowerCase();
|
||||||
return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value);
|
return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value);
|
||||||
@ -72,4 +72,4 @@ const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSe
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ApplyDNSProviderPicker);
|
export default memo(ACMEDns01ProviderPicker);
|
@ -2,21 +2,21 @@ import { memo, useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
||||||
|
|
||||||
import { type ApplyDNSProvider, applyDNSProvidersMap } from "@/domain/provider";
|
import { type ACMEDns01Provider, acmeDns01ProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type ApplyDNSProviderSelectProps = Omit<
|
export type ACMEDns01ProviderSelectProps = Omit<
|
||||||
SelectProps,
|
SelectProps,
|
||||||
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||||
> & {
|
> & {
|
||||||
filter?: (record: ApplyDNSProvider) => boolean;
|
filter?: (record: ACMEDns01Provider) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApplyDNSProviderSelect = ({ filter, ...props }: ApplyDNSProviderSelectProps) => {
|
const ACMEDns01ProviderSelect = ({ filter, ...props }: ACMEDns01ProviderSelectProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: ApplyDNSProvider }>>([]);
|
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: ACMEDns01Provider }>>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const allItems = Array.from(applyDNSProvidersMap.values());
|
const allItems = Array.from(acmeDns01ProvidersMap.values());
|
||||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||||
setOptions(
|
setOptions(
|
||||||
filteredItems.map((item) => ({
|
filteredItems.map((item) => ({
|
||||||
@ -29,7 +29,7 @@ const ApplyDNSProviderSelect = ({ filter, ...props }: ApplyDNSProviderSelectProp
|
|||||||
}, [filter]);
|
}, [filter]);
|
||||||
|
|
||||||
const renderOption = (key: string) => {
|
const renderOption = (key: string) => {
|
||||||
const provider = applyDNSProvidersMap.get(key);
|
const provider = acmeDns01ProvidersMap.get(key);
|
||||||
return (
|
return (
|
||||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||||
<Avatar src={provider?.icon} size="small" />
|
<Avatar src={provider?.icon} size="small" />
|
||||||
@ -64,4 +64,4 @@ const ApplyDNSProviderSelect = ({ filter, ...props }: ApplyDNSProviderSelectProp
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ApplyDNSProviderSelect);
|
export default memo(ACMEDns01ProviderSelect);
|
@ -2,28 +2,28 @@ import { memo, useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
||||||
|
|
||||||
import { type ApplyCAProvider, applyCAProvidersMap } from "@/domain/provider";
|
import { type CAProvider, caProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type ApplyCAProviderSelectProps = Omit<
|
export type CAProviderSelectProps = Omit<
|
||||||
SelectProps,
|
SelectProps,
|
||||||
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||||
> & {
|
> & {
|
||||||
filter?: (record: ApplyCAProvider) => boolean;
|
filter?: (record: CAProvider) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApplyCAProviderSelect = ({ filter, ...props }: ApplyCAProviderSelectProps) => {
|
const CAProviderSelect = ({ filter, ...props }: CAProviderSelectProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: ApplyCAProvider }>>([]);
|
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: CAProvider }>>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const allItems = Array.from(applyCAProvidersMap.values());
|
const allItems = Array.from(caProvidersMap.values());
|
||||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||||
setOptions([
|
setOptions([
|
||||||
{
|
{
|
||||||
key: "",
|
key: "",
|
||||||
value: "",
|
value: "",
|
||||||
label: "provider.default_ca_provider.label",
|
label: t("provider.default_ca_provider.label"),
|
||||||
data: {} as ApplyCAProvider,
|
data: {} as CAProvider,
|
||||||
},
|
},
|
||||||
...filteredItems.map((item) => ({
|
...filteredItems.map((item) => ({
|
||||||
key: item.type,
|
key: item.type,
|
||||||
@ -45,7 +45,7 @@ const ApplyCAProviderSelect = ({ filter, ...props }: ApplyCAProviderSelectProps)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = applyCAProvidersMap.get(key);
|
const provider = caProvidersMap.get(key);
|
||||||
return (
|
return (
|
||||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||||
<Avatar src={provider?.icon} size="small" />
|
<Avatar src={provider?.icon} size="small" />
|
||||||
@ -80,4 +80,4 @@ const ApplyCAProviderSelect = ({ filter, ...props }: ApplyCAProviderSelectProps)
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ApplyCAProviderSelect);
|
export default memo(CAProviderSelect);
|
@ -3,9 +3,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Tabs, Tooltip, Typography } from "antd";
|
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Tabs, Tooltip, Typography } from "antd";
|
||||||
|
|
||||||
import Show from "@/components/Show";
|
import Show from "@/components/Show";
|
||||||
import { DEPLOY_CATEGORIES, deployProvidersMap } from "@/domain/provider";
|
import { DEPLOYMENT_CATEGORIES, deploymentProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type DeployProviderPickerProps = {
|
export type DeploymentProviderPickerProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
@ -13,10 +13,10 @@ export type DeployProviderPickerProps = {
|
|||||||
onSelect?: (value: string) => void;
|
onSelect?: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: DeployProviderPickerProps) => {
|
const DeploymentProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: DeploymentProviderPickerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [category, setCategory] = useState<string>(DEPLOY_CATEGORIES.ALL);
|
const [category, setCategory] = useState<string>(DEPLOYMENT_CATEGORIES.ALL);
|
||||||
|
|
||||||
const [keyword, setKeyword] = useState<string>();
|
const [keyword, setKeyword] = useState<string>();
|
||||||
const keywordInputRef = useRef<InputRef>(null);
|
const keywordInputRef = useRef<InputRef>(null);
|
||||||
@ -27,9 +27,9 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const providers = useMemo(() => {
|
const providers = useMemo(() => {
|
||||||
return Array.from(deployProvidersMap.values())
|
return Array.from(deploymentProvidersMap.values())
|
||||||
.filter((provider) => {
|
.filter((provider) => {
|
||||||
if (category && category !== DEPLOY_CATEGORIES.ALL) {
|
if (category && category !== DEPLOYMENT_CATEGORIES.ALL) {
|
||||||
return provider.category === category;
|
return provider.category === category;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,17 +56,17 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele
|
|||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Flex>
|
<Flex>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultActiveKey={DEPLOY_CATEGORIES.ALL}
|
defaultActiveKey={DEPLOYMENT_CATEGORIES.ALL}
|
||||||
items={[
|
items={[
|
||||||
DEPLOY_CATEGORIES.ALL,
|
DEPLOYMENT_CATEGORIES.ALL,
|
||||||
DEPLOY_CATEGORIES.CDN,
|
DEPLOYMENT_CATEGORIES.CDN,
|
||||||
DEPLOY_CATEGORIES.STORAGE,
|
DEPLOYMENT_CATEGORIES.STORAGE,
|
||||||
DEPLOY_CATEGORIES.LOADBALANCE,
|
DEPLOYMENT_CATEGORIES.LOADBALANCE,
|
||||||
DEPLOY_CATEGORIES.FIREWALL,
|
DEPLOYMENT_CATEGORIES.FIREWALL,
|
||||||
DEPLOY_CATEGORIES.AV,
|
DEPLOYMENT_CATEGORIES.AV,
|
||||||
DEPLOY_CATEGORIES.SERVERLESS,
|
DEPLOYMENT_CATEGORIES.SERVERLESS,
|
||||||
DEPLOY_CATEGORIES.WEBSITE,
|
DEPLOYMENT_CATEGORIES.WEBSITE,
|
||||||
DEPLOY_CATEGORIES.OTHER,
|
DEPLOYMENT_CATEGORIES.OTHER,
|
||||||
].map((key) => ({
|
].map((key) => ({
|
||||||
key: key,
|
key: key,
|
||||||
label: t(`provider.category.${key}`),
|
label: t(`provider.category.${key}`),
|
||||||
@ -110,4 +110,4 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(DeployProviderPicker);
|
export default memo(DeploymentProviderPicker);
|
@ -2,21 +2,21 @@ import { memo, useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
||||||
|
|
||||||
import { type DeployProvider, deployProvidersMap } from "@/domain/provider";
|
import { type DeploymentProvider, deploymentProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type DeployProviderSelectProps = Omit<
|
export type DeploymentProviderSelectProps = Omit<
|
||||||
SelectProps,
|
SelectProps,
|
||||||
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||||
> & {
|
> & {
|
||||||
filter?: (record: DeployProvider) => boolean;
|
filter?: (record: DeploymentProvider) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) => {
|
const DeploymentProviderSelect = ({ filter, ...props }: DeploymentProviderSelectProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: DeployProvider }>>([]);
|
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: DeploymentProvider }>>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const allItems = Array.from(deployProvidersMap.values());
|
const allItems = Array.from(deploymentProvidersMap.values());
|
||||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||||
setOptions(
|
setOptions(
|
||||||
filteredItems.map((item) => ({
|
filteredItems.map((item) => ({
|
||||||
@ -29,7 +29,7 @@ const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) =
|
|||||||
}, [filter]);
|
}, [filter]);
|
||||||
|
|
||||||
const renderOption = (key: string) => {
|
const renderOption = (key: string) => {
|
||||||
const provider = deployProvidersMap.get(key);
|
const provider = deploymentProvidersMap.get(key);
|
||||||
return (
|
return (
|
||||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||||
<Avatar src={provider?.icon} size="small" />
|
<Avatar src={provider?.icon} size="small" />
|
||||||
@ -64,4 +64,4 @@ const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) =
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(DeployProviderSelect);
|
export default memo(DeploymentProviderSelect);
|
67
ui/src/components/provider/NotificationProviderSelect.tsx
Normal file
67
ui/src/components/provider/NotificationProviderSelect.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { memo, useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
||||||
|
|
||||||
|
import { type NotificationProvider, notificationProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
|
export type NotificationProviderSelectProps = Omit<
|
||||||
|
SelectProps,
|
||||||
|
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||||
|
> & {
|
||||||
|
filter?: (record: NotificationProvider) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NotificationProviderSelect = ({ filter, ...props }: NotificationProviderSelectProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: NotificationProvider }>>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const allItems = Array.from(notificationProvidersMap.values());
|
||||||
|
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||||
|
setOptions(
|
||||||
|
filteredItems.map((item) => ({
|
||||||
|
key: item.type,
|
||||||
|
value: item.type,
|
||||||
|
label: t(item.name),
|
||||||
|
data: item,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, [filter]);
|
||||||
|
|
||||||
|
const renderOption = (key: string) => {
|
||||||
|
const provider = notificationProvidersMap.get(key);
|
||||||
|
return (
|
||||||
|
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||||
|
<Avatar src={provider?.icon} size="small" />
|
||||||
|
<Typography.Text className="leading-loose" ellipsis>
|
||||||
|
{t(provider?.name ?? "")}
|
||||||
|
</Typography.Text>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...props}
|
||||||
|
filterOption={(inputValue, option) => {
|
||||||
|
if (!option) return false;
|
||||||
|
|
||||||
|
const value = inputValue.toLowerCase();
|
||||||
|
return option.value.toLowerCase().includes(value) || option.label.toLowerCase().includes(value);
|
||||||
|
}}
|
||||||
|
labelRender={({ label, value }) => {
|
||||||
|
if (!label) {
|
||||||
|
return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderOption(value as string);
|
||||||
|
}}
|
||||||
|
options={options}
|
||||||
|
optionFilterProp={undefined}
|
||||||
|
optionLabelProp={undefined}
|
||||||
|
optionRender={(option) => renderOption(option.data.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(NotificationProviderSelect);
|
@ -193,6 +193,7 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
|
|||||||
const NEWLINE = "\n";
|
const NEWLINE = "\n";
|
||||||
const logstr = listData
|
const logstr = listData
|
||||||
.map((group) => {
|
.map((group) => {
|
||||||
|
const escape = (str: string) => str.replaceAll("\r", "\\r").replaceAll("\n", "\\n");
|
||||||
return (
|
return (
|
||||||
group.name +
|
group.name +
|
||||||
NEWLINE +
|
NEWLINE +
|
||||||
@ -200,8 +201,9 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
|
|||||||
.map((record) => {
|
.map((record) => {
|
||||||
const datetime = dayjs(record.timestamp).format("YYYY-MM-DDTHH:mm:ss.SSSZ");
|
const datetime = dayjs(record.timestamp).format("YYYY-MM-DDTHH:mm:ss.SSSZ");
|
||||||
const level = record.level;
|
const level = record.level;
|
||||||
const message = record.message.trim().replaceAll("\r", "\\r").replaceAll("\n", "\\n");
|
const message = record.message;
|
||||||
return `[${datetime}] [${level}] ${message}`;
|
const data = record.data && Object.keys(record.data).length > 0 ? JSON.stringify(record.data) : "";
|
||||||
|
return `[${datetime}] [${level}] ${escape(message)} ${escape(data)}`.trim();
|
||||||
})
|
})
|
||||||
.join(NEWLINE)
|
.join(NEWLINE)
|
||||||
);
|
);
|
||||||
|
@ -31,9 +31,10 @@ import AccessEditModal from "@/components/access/AccessEditModal";
|
|||||||
import AccessSelect from "@/components/access/AccessSelect";
|
import AccessSelect from "@/components/access/AccessSelect";
|
||||||
import ModalForm from "@/components/ModalForm";
|
import ModalForm from "@/components/ModalForm";
|
||||||
import MultipleInput from "@/components/MultipleInput";
|
import MultipleInput from "@/components/MultipleInput";
|
||||||
import ApplyCAProviderSelect from "@/components/provider/ApplyCAProviderSelect";
|
import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect";
|
||||||
import ApplyDNSProviderSelect from "@/components/provider/ApplyDNSProviderSelect";
|
import CAProviderSelect from "@/components/provider/CAProviderSelect";
|
||||||
import { ACCESS_USAGES, APPLY_DNS_PROVIDERS, accessProvidersMap, applyCAProvidersMap, applyDNSProvidersMap } from "@/domain/provider";
|
import Show from "@/components/Show";
|
||||||
|
import { ACCESS_USAGES, ACME_DNS01_PROVIDERS, accessProvidersMap, acmeDns01ProvidersMap, caProvidersMap } from "@/domain/provider";
|
||||||
import { type WorkflowNodeConfigForApply } from "@/domain/workflow";
|
import { type WorkflowNodeConfigForApply } from "@/domain/workflow";
|
||||||
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
|
||||||
import { useAccessesStore } from "@/stores/access";
|
import { useAccessesStore } from "@/stores/access";
|
||||||
@ -98,7 +99,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
.refine((v) => {
|
.refine((v) => {
|
||||||
if (!fieldCAProvider) return true;
|
if (!fieldCAProvider) return true;
|
||||||
|
|
||||||
const provider = applyCAProvidersMap.get(fieldCAProvider);
|
const provider = caProvidersMap.get(fieldCAProvider);
|
||||||
return !!provider?.builtin || !!v;
|
return !!provider?.builtin || !!v;
|
||||||
}, t("workflow_node.apply.form.ca_provider_access.placeholder")),
|
}, t("workflow_node.apply.form.ca_provider_access.placeholder")),
|
||||||
caProviderConfig: z.any().nullish(),
|
caProviderConfig: z.any().nullish(),
|
||||||
@ -154,7 +155,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
// 如果对应多个(如 AWS 的 Route53、Lightsail,腾讯云的 DNS、EdgeOne 等),则显示。
|
// 如果对应多个(如 AWS 的 Route53、Lightsail,腾讯云的 DNS、EdgeOne 等),则显示。
|
||||||
if (fieldProviderAccessId) {
|
if (fieldProviderAccessId) {
|
||||||
const access = accesses.find((e) => e.id === fieldProviderAccessId);
|
const access = accesses.find((e) => e.id === fieldProviderAccessId);
|
||||||
const providers = Array.from(applyDNSProvidersMap.values()).filter((e) => e.provider === access?.provider);
|
const providers = Array.from(acmeDns01ProvidersMap.values()).filter((e) => e.provider === access?.provider);
|
||||||
setShowProvider(providers.length > 1);
|
setShowProvider(providers.length > 1);
|
||||||
} else {
|
} else {
|
||||||
setShowProvider(false);
|
setShowProvider(false);
|
||||||
@ -165,7 +166,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 内置的 CA 提供商(如 Let's Encrypt)无需显示授权信息字段
|
// 内置的 CA 提供商(如 Let's Encrypt)无需显示授权信息字段
|
||||||
if (fieldCAProvider) {
|
if (fieldCAProvider) {
|
||||||
const provider = applyCAProvidersMap.get(fieldCAProvider);
|
const provider = caProvidersMap.get(fieldCAProvider);
|
||||||
setShowCAProviderAccess(!provider?.builtin);
|
setShowCAProviderAccess(!provider?.builtin);
|
||||||
} else {
|
} else {
|
||||||
setShowCAProviderAccess(false);
|
setShowCAProviderAccess(false);
|
||||||
@ -187,16 +188,16 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
NOTICE: If you add new child component, please keep ASCII order.
|
NOTICE: If you add new child component, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch (fieldProvider) {
|
switch (fieldProvider) {
|
||||||
case APPLY_DNS_PROVIDERS.AWS:
|
case ACME_DNS01_PROVIDERS.AWS:
|
||||||
case APPLY_DNS_PROVIDERS.AWS_ROUTE53:
|
case ACME_DNS01_PROVIDERS.AWS_ROUTE53:
|
||||||
return <ApplyNodeConfigFormAWSRoute53Config {...nestedFormProps} />;
|
return <ApplyNodeConfigFormAWSRoute53Config {...nestedFormProps} />;
|
||||||
case APPLY_DNS_PROVIDERS.HUAWEICLOUD:
|
case ACME_DNS01_PROVIDERS.HUAWEICLOUD:
|
||||||
case APPLY_DNS_PROVIDERS.HUAWEICLOUD_DNS:
|
case ACME_DNS01_PROVIDERS.HUAWEICLOUD_DNS:
|
||||||
return <ApplyNodeConfigFormHuaweiCloudDNSConfig {...nestedFormProps} />;
|
return <ApplyNodeConfigFormHuaweiCloudDNSConfig {...nestedFormProps} />;
|
||||||
case APPLY_DNS_PROVIDERS.JDCLOUD:
|
case ACME_DNS01_PROVIDERS.JDCLOUD:
|
||||||
case APPLY_DNS_PROVIDERS.JDCLOUD_DNS:
|
case ACME_DNS01_PROVIDERS.JDCLOUD_DNS:
|
||||||
return <ApplyNodeConfigFormJDCloudDNSConfig {...nestedFormProps} />;
|
return <ApplyNodeConfigFormJDCloudDNSConfig {...nestedFormProps} />;
|
||||||
case APPLY_DNS_PROVIDERS.TENCENTCLOUD_EO:
|
case ACME_DNS01_PROVIDERS.TENCENTCLOUD_EO:
|
||||||
return <ApplyNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
|
return <ApplyNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
|
||||||
}
|
}
|
||||||
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
|
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
|
||||||
@ -209,7 +210,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId);
|
formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
} else {
|
} else {
|
||||||
if (applyDNSProvidersMap.get(fieldProvider)?.provider !== applyDNSProvidersMap.get(value)?.provider) {
|
if (acmeDns01ProvidersMap.get(fieldProvider)?.provider !== acmeDns01ProvidersMap.get(value)?.provider) {
|
||||||
formInst.setFieldValue("providerAccessId", undefined);
|
formInst.setFieldValue("providerAccessId", undefined);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
}
|
}
|
||||||
@ -217,11 +218,9 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleProviderAccessSelect = (value: string) => {
|
const handleProviderAccessSelect = (value: string) => {
|
||||||
if (fieldProviderAccessId === value) return;
|
|
||||||
|
|
||||||
// 切换授权信息时联动 DNS 提供商
|
// 切换授权信息时联动 DNS 提供商
|
||||||
const access = accesses.find((access) => access.id === value);
|
const access = accesses.find((access) => access.id === value);
|
||||||
const provider = Array.from(applyDNSProvidersMap.values()).find((provider) => provider.provider === access?.provider);
|
const provider = Array.from(acmeDns01ProvidersMap.values()).find((provider) => provider.provider === access?.provider);
|
||||||
if (fieldProvider !== provider?.type) {
|
if (fieldProvider !== provider?.type) {
|
||||||
formInst.setFieldValue("provider", provider?.type);
|
formInst.setFieldValue("provider", provider?.type);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
@ -229,8 +228,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCAProviderSelect = (value?: string | undefined) => {
|
const handleCAProviderSelect = (value?: string | undefined) => {
|
||||||
if (fieldCAProvider === value) return;
|
|
||||||
|
|
||||||
// 切换 CA 提供商时联动授权信息
|
// 切换 CA 提供商时联动授权信息
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -242,7 +239,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
formInst.setFieldValue("caProviderAccessId", initialValues?.caProviderAccessId);
|
formInst.setFieldValue("caProviderAccessId", initialValues?.caProviderAccessId);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
} else {
|
} else {
|
||||||
if (applyCAProvidersMap.get(fieldCAProvider)?.provider !== applyCAProvidersMap.get(value!)?.provider) {
|
if (caProvidersMap.get(fieldCAProvider)?.provider !== caProvidersMap.get(value!)?.provider) {
|
||||||
formInst.setFieldValue("caProviderAccessId", undefined);
|
formInst.setFieldValue("caProviderAccessId", undefined);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
}
|
}
|
||||||
@ -327,7 +324,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="provider" label={t("workflow_node.apply.form.provider.label")} hidden={!showProvider} rules={[formRule]}>
|
<Form.Item name="provider" label={t("workflow_node.apply.form.provider.label")} hidden={!showProvider} rules={[formRule]}>
|
||||||
<ApplyDNSProviderSelect
|
<ACMEDns01ProviderSelect
|
||||||
disabled={!showProvider}
|
disabled={!showProvider}
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
if (fieldProviderAccessId) {
|
if (fieldProviderAccessId) {
|
||||||
@ -355,7 +352,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<AccessEditModal
|
<AccessEditModal
|
||||||
range="both-dns-hosting"
|
|
||||||
scene="add"
|
scene="add"
|
||||||
trigger={
|
trigger={
|
||||||
<Button size="small" type="link">
|
<Button size="small" type="link">
|
||||||
@ -363,10 +359,12 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
<PlusOutlinedIcon className="text-xs" />
|
<PlusOutlinedIcon className="text-xs" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
usage="both-dns-hosting"
|
||||||
afterSubmit={(record) => {
|
afterSubmit={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
|
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
|
||||||
formInst.setFieldValue("providerAccessId", record.id);
|
formInst.setFieldValue("providerAccessId", record.id);
|
||||||
|
handleProviderAccessSelect(record.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -376,6 +374,8 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
<Form.Item name="providerAccessId" rules={[formRule]}>
|
<Form.Item name="providerAccessId" rules={[formRule]}>
|
||||||
<AccessSelect
|
<AccessSelect
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
|
if (record.reserve) return false;
|
||||||
|
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
return !!provider?.usages?.includes(ACCESS_USAGES.DNS);
|
return !!provider?.usages?.includes(ACCESS_USAGES.DNS);
|
||||||
}}
|
}}
|
||||||
@ -398,19 +398,23 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
<Form.Item className="mb-0">
|
<Form.Item className="mb-0">
|
||||||
<label className="mb-1 block">
|
<label className="mb-1 block">
|
||||||
<div className="flex w-full items-center justify-between gap-4">
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
<div className="max-w-full grow truncate">{t("workflow_node.apply.form.ca_provider.label")}</div>
|
<div className="max-w-full grow truncate">
|
||||||
|
<span>{t("workflow_node.apply.form.ca_provider.label")}</span>
|
||||||
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<Link className="ant-typography" to="/settings/ssl-provider" target="_blank">
|
<Show when={!fieldCAProvider}>
|
||||||
<Button size="small" type="link">
|
<Link className="ant-typography" to="/settings/ssl-provider" target="_blank">
|
||||||
{t("workflow_node.apply.form.ca_provider.button")}
|
<Button size="small" type="link">
|
||||||
<RightOutlinedIcon className="text-xs" />
|
{t("workflow_node.apply.form.ca_provider.button")}
|
||||||
</Button>
|
<RightOutlinedIcon className="text-xs" />
|
||||||
</Link>
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<Form.Item name="caProvider" rules={[formRule]}>
|
<Form.Item name="caProvider" rules={[formRule]}>
|
||||||
<ApplyCAProviderSelect
|
<CAProviderSelect
|
||||||
allowClear
|
allowClear
|
||||||
placeholder={t("workflow_node.apply.form.ca_provider.placeholder")}
|
placeholder={t("workflow_node.apply.form.ca_provider.placeholder")}
|
||||||
showSearch
|
showSearch
|
||||||
@ -428,8 +432,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<AccessEditModal
|
<AccessEditModal
|
||||||
data={{ provider: applyCAProvidersMap.get(fieldCAProvider!)?.provider }}
|
data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
|
||||||
range="ca-only"
|
|
||||||
scene="add"
|
scene="add"
|
||||||
trigger={
|
trigger={
|
||||||
<Button size="small" type="link">
|
<Button size="small" type="link">
|
||||||
@ -437,6 +440,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
<PlusOutlinedIcon className="text-xs" />
|
<PlusOutlinedIcon className="text-xs" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
usage="ca-only"
|
||||||
afterSubmit={(record) => {
|
afterSubmit={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
|
if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
|
||||||
@ -450,9 +454,8 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
|||||||
<Form.Item name="caProviderAccessId" rules={[formRule]}>
|
<Form.Item name="caProviderAccessId" rules={[formRule]}>
|
||||||
<AccessSelect
|
<AccessSelect
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
if (fieldCAProvider) {
|
if (record.reserve !== "ca") return false;
|
||||||
return applyCAProvidersMap.get(fieldCAProvider)?.provider === record.provider;
|
if (fieldCAProvider) return caProvidersMap.get(fieldCAProvider)?.provider === record.provider;
|
||||||
}
|
|
||||||
|
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
return !!provider?.usages?.includes(ACCESS_USAGES.CA);
|
return !!provider?.usages?.includes(ACCESS_USAGES.CA);
|
||||||
|
@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Avatar, Flex, Typography } from "antd";
|
import { Avatar, Flex, Typography } from "antd";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
|
||||||
import { deployProvidersMap } from "@/domain/provider";
|
import { deploymentProvidersMap } from "@/domain/provider";
|
||||||
import { type WorkflowNodeConfigForDeploy, WorkflowNodeType } from "@/domain/workflow";
|
import { type WorkflowNodeConfigForDeploy, WorkflowNodeType } from "@/domain/workflow";
|
||||||
import { useZustandShallowSelector } from "@/hooks";
|
import { useZustandShallowSelector } from "@/hooks";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
@ -43,7 +43,7 @@ const DeployNode = ({ node, disabled }: DeployNodeProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const config = (node.config as WorkflowNodeConfigForDeploy) ?? {};
|
const config = (node.config as WorkflowNodeConfigForDeploy) ?? {};
|
||||||
const provider = deployProvidersMap.get(config.provider);
|
const provider = deploymentProvidersMap.get(config.provider);
|
||||||
return (
|
return (
|
||||||
<Flex className="size-full overflow-hidden" align="center" gap={8}>
|
<Flex className="size-full overflow-hidden" align="center" gap={8}>
|
||||||
<Avatar src={provider?.icon} size="small" />
|
<Avatar src={provider?.icon} size="small" />
|
||||||
|
@ -7,10 +7,10 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import AccessEditModal from "@/components/access/AccessEditModal";
|
import AccessEditModal from "@/components/access/AccessEditModal";
|
||||||
import AccessSelect from "@/components/access/AccessSelect";
|
import AccessSelect from "@/components/access/AccessSelect";
|
||||||
import DeployProviderPicker from "@/components/provider/DeployProviderPicker.tsx";
|
import DeploymentProviderPicker from "@/components/provider/DeploymentProviderPicker.tsx";
|
||||||
import DeployProviderSelect from "@/components/provider/DeployProviderSelect.tsx";
|
import DeploymentProviderSelect from "@/components/provider/DeploymentProviderSelect.tsx";
|
||||||
import Show from "@/components/Show";
|
import Show from "@/components/Show";
|
||||||
import { ACCESS_USAGES, DEPLOY_PROVIDERS, accessProvidersMap, deployProvidersMap } from "@/domain/provider";
|
import { ACCESS_USAGES, DEPLOYMENT_PROVIDERS, accessProvidersMap, deploymentProvidersMap } from "@/domain/provider";
|
||||||
import { type WorkflowNode, type WorkflowNodeConfigForDeploy } from "@/domain/workflow";
|
import { type WorkflowNode, type WorkflowNodeConfigForDeploy } from "@/domain/workflow";
|
||||||
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
@ -133,7 +133,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
.refine((v) => {
|
.refine((v) => {
|
||||||
if (!fieldProvider) return true;
|
if (!fieldProvider) return true;
|
||||||
|
|
||||||
const provider = deployProvidersMap.get(fieldProvider);
|
const provider = deploymentProvidersMap.get(fieldProvider);
|
||||||
return !!provider?.builtin || !!v;
|
return !!provider?.builtin || !!v;
|
||||||
}, t("workflow_node.deploy.form.provider_access.placeholder")),
|
}, t("workflow_node.deploy.form.provider_access.placeholder")),
|
||||||
providerConfig: z.any().nullish(),
|
providerConfig: z.any().nullish(),
|
||||||
@ -151,7 +151,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 内置的部署提供商(如本地部署)无需显示授权信息字段
|
// 内置的部署提供商(如本地部署)无需显示授权信息字段
|
||||||
if (fieldProvider) {
|
if (fieldProvider) {
|
||||||
const provider = deployProvidersMap.get(fieldProvider);
|
const provider = deploymentProvidersMap.get(fieldProvider);
|
||||||
setShowProviderAccess(!provider?.builtin);
|
setShowProviderAccess(!provider?.builtin);
|
||||||
} else {
|
} else {
|
||||||
setShowProviderAccess(false);
|
setShowProviderAccess(false);
|
||||||
@ -173,145 +173,145 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
NOTICE: If you add new child component, please keep ASCII order.
|
NOTICE: If you add new child component, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch (fieldProvider) {
|
switch (fieldProvider) {
|
||||||
case DEPLOY_PROVIDERS["1PANEL_CONSOLE"]:
|
case DEPLOYMENT_PROVIDERS["1PANEL_CONSOLE"]:
|
||||||
return <DeployNodeConfigForm1PanelConsoleConfig {...nestedFormProps} />;
|
return <DeployNodeConfigForm1PanelConsoleConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS["1PANEL_SITE"]:
|
case DEPLOYMENT_PROVIDERS["1PANEL_SITE"]:
|
||||||
return <DeployNodeConfigForm1PanelSiteConfig {...nestedFormProps} />;
|
return <DeployNodeConfigForm1PanelSiteConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_ALB:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_ALB:
|
||||||
return <DeployNodeConfigFormAliyunALBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunALBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_APIGW:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_APIGW:
|
||||||
return <DeployNodeConfigFormAliyunAPIGWConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunAPIGWConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_CAS:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_CAS:
|
||||||
return <DeployNodeConfigFormAliyunCASConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunCASConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_CAS_DEPLOY:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_CAS_DEPLOY:
|
||||||
return <DeployNodeConfigFormAliyunCASDeployConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunCASDeployConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_CLB:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_CLB:
|
||||||
return <DeployNodeConfigFormAliyunCLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunCLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_CDN:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_CDN:
|
||||||
return <DeployNodeConfigFormAliyunCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_DCDN:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_DCDN:
|
||||||
return <DeployNodeConfigFormAliyunDCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunDCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_ESA:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_ESA:
|
||||||
return <DeployNodeConfigFormAliyunESAConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunESAConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_FC:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_FC:
|
||||||
return <DeployNodeConfigFormAliyunFCConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunFCConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_LIVE:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_LIVE:
|
||||||
return <DeployNodeConfigFormAliyunLiveConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunLiveConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_NLB:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_NLB:
|
||||||
return <DeployNodeConfigFormAliyunNLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunNLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_OSS:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_OSS:
|
||||||
return <DeployNodeConfigFormAliyunOSSConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunOSSConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_VOD:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_VOD:
|
||||||
return <DeployNodeConfigFormAliyunVODConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunVODConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.ALIYUN_WAF:
|
case DEPLOYMENT_PROVIDERS.ALIYUN_WAF:
|
||||||
return <DeployNodeConfigFormAliyunWAFConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAliyunWAFConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.AWS_ACM:
|
case DEPLOYMENT_PROVIDERS.AWS_ACM:
|
||||||
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.AWS_CLOUDFRONT:
|
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
|
||||||
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.AZURE_KEYVAULT:
|
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
|
||||||
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAIDUCLOUD_APPBLB:
|
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:
|
||||||
return <DeployNodeConfigFormBaiduCloudAppBLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaiduCloudAppBLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAIDUCLOUD_BLB:
|
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_BLB:
|
||||||
return <DeployNodeConfigFormBaiduCloudBLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaiduCloudBLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAIDUCLOUD_CDN:
|
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_CDN:
|
||||||
return <DeployNodeConfigFormBaiduCloudCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaiduCloudCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAISHAN_CDN:
|
case DEPLOYMENT_PROVIDERS.BAISHAN_CDN:
|
||||||
return <DeployNodeConfigFormBaishanCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaishanCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAOTAPANEL_CONSOLE:
|
case DEPLOYMENT_PROVIDERS.BAOTAPANEL_CONSOLE:
|
||||||
return <DeployNodeConfigFormBaotaPanelConsoleConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaotaPanelConsoleConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BAOTAPANEL_SITE:
|
case DEPLOYMENT_PROVIDERS.BAOTAPANEL_SITE:
|
||||||
return <DeployNodeConfigFormBaotaPanelSiteConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBaotaPanelSiteConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BUNNY_CDN:
|
case DEPLOYMENT_PROVIDERS.BUNNY_CDN:
|
||||||
return <DeployNodeConfigFormBunnyCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBunnyCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.BYTEPLUS_CDN:
|
case DEPLOYMENT_PROVIDERS.BYTEPLUS_CDN:
|
||||||
return <DeployNodeConfigFormBytePlusCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormBytePlusCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.CDNFLY:
|
case DEPLOYMENT_PROVIDERS.CDNFLY:
|
||||||
return <DeployNodeConfigFormCdnflyConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormCdnflyConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.DOGECLOUD_CDN:
|
case DEPLOYMENT_PROVIDERS.DOGECLOUD_CDN:
|
||||||
return <DeployNodeConfigFormDogeCloudCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormDogeCloudCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.EDGIO_APPLICATIONS:
|
case DEPLOYMENT_PROVIDERS.EDGIO_APPLICATIONS:
|
||||||
return <DeployNodeConfigFormEdgioApplicationsConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormEdgioApplicationsConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.GCORE_CDN:
|
case DEPLOYMENT_PROVIDERS.GCORE_CDN:
|
||||||
return <DeployNodeConfigFormGcoreCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormGcoreCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.HUAWEICLOUD_CDN:
|
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_CDN:
|
||||||
return <DeployNodeConfigFormHuaweiCloudCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormHuaweiCloudCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.HUAWEICLOUD_ELB:
|
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_ELB:
|
||||||
return <DeployNodeConfigFormHuaweiCloudELBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormHuaweiCloudELBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.HUAWEICLOUD_WAF:
|
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF:
|
||||||
return <DeployNodeConfigFormHuaweiCloudWAFConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormHuaweiCloudWAFConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.JDCLOUD_ALB:
|
case DEPLOYMENT_PROVIDERS.JDCLOUD_ALB:
|
||||||
return <DeployNodeConfigFormJDCloudALBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudALBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.JDCLOUD_CDN:
|
case DEPLOYMENT_PROVIDERS.JDCLOUD_CDN:
|
||||||
return <DeployNodeConfigFormJDCloudCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.JDCLOUD_LIVE:
|
case DEPLOYMENT_PROVIDERS.JDCLOUD_LIVE:
|
||||||
return <DeployNodeConfigFormJDCloudLiveConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudLiveConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.JDCLOUD_VOD:
|
case DEPLOYMENT_PROVIDERS.JDCLOUD_VOD:
|
||||||
return <DeployNodeConfigFormJDCloudVODConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudVODConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.KUBERNETES_SECRET:
|
case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET:
|
||||||
return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.LOCAL:
|
case DEPLOYMENT_PROVIDERS.LOCAL:
|
||||||
return <DeployNodeConfigFormLocalConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormLocalConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.QINIU_CDN:
|
case DEPLOYMENT_PROVIDERS.QINIU_CDN:
|
||||||
return <DeployNodeConfigFormQiniuCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormQiniuCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.QINIU_KODO:
|
case DEPLOYMENT_PROVIDERS.QINIU_KODO:
|
||||||
return <DeployNodeConfigFormQiniuKodoConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormQiniuKodoConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.QINIU_PILI:
|
case DEPLOYMENT_PROVIDERS.QINIU_PILI:
|
||||||
return <DeployNodeConfigFormQiniuPiliConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormQiniuPiliConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.RAINYUN_RCDN:
|
case DEPLOYMENT_PROVIDERS.RAINYUN_RCDN:
|
||||||
return <DeployNodeConfigFormRainYunRCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormRainYunRCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.SAFELINE:
|
case DEPLOYMENT_PROVIDERS.SAFELINE:
|
||||||
return <DeployNodeConfigFormSafeLineConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormSafeLineConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.SSH:
|
case DEPLOYMENT_PROVIDERS.SSH:
|
||||||
return <DeployNodeConfigFormSSHConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormSSHConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_CDN:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CDN:
|
||||||
return <DeployNodeConfigFormTencentCloudCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_CLB:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CLB:
|
||||||
return <DeployNodeConfigFormTencentCloudCLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudCLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_COS:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_COS:
|
||||||
return <DeployNodeConfigFormTencentCloudCOSConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudCOSConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_CSS:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CSS:
|
||||||
return <DeployNodeConfigFormTencentCloudCSSConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudCSSConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_ECDN:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_ECDN:
|
||||||
return <DeployNodeConfigFormTencentCloudECDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudECDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_EO:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_EO:
|
||||||
return <DeployNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_SCF:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SCF:
|
||||||
return <DeployNodeConfigFormTencentCloudSCFConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudSCFConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY:
|
||||||
return <DeployNodeConfigFormTencentCloudSSLDeployConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudSSLDeployConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_VOD:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_VOD:
|
||||||
return <DeployNodeConfigFormTencentCloudVODConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudVODConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.TENCENTCLOUD_WAF:
|
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_WAF:
|
||||||
return <DeployNodeConfigFormTencentCloudWAFConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormTencentCloudWAFConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.UCLOUD_UCDN:
|
case DEPLOYMENT_PROVIDERS.UCLOUD_UCDN:
|
||||||
return <DeployNodeConfigFormUCloudUCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormUCloudUCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.UCLOUD_US3:
|
case DEPLOYMENT_PROVIDERS.UCLOUD_US3:
|
||||||
return <DeployNodeConfigFormUCloudUS3Config {...nestedFormProps} />;
|
return <DeployNodeConfigFormUCloudUS3Config {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.UPYUN_CDN:
|
case DEPLOYMENT_PROVIDERS.UPYUN_CDN:
|
||||||
return <DeployNodeConfigFormUpyunCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormUpyunCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.UPYUN_FILE:
|
case DEPLOYMENT_PROVIDERS.UPYUN_FILE:
|
||||||
return <DeployNodeConfigFormUpyunFileConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormUpyunFileConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_ALB:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_ALB:
|
||||||
return <DeployNodeConfigFormVolcEngineALBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineALBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_CDN:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CDN:
|
||||||
return <DeployNodeConfigFormVolcEngineCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_CERTCENTER:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CERTCENTER:
|
||||||
return <DeployNodeConfigFormVolcEngineCertCenterConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineCertCenterConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_CLB:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CLB:
|
||||||
return <DeployNodeConfigFormVolcEngineCLBConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineCLBConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_DCDN:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_DCDN:
|
||||||
return <DeployNodeConfigFormVolcEngineDCDNConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineDCDNConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_IMAGEX:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_IMAGEX:
|
||||||
return <DeployNodeConfigFormVolcEngineImageXConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineImageXConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_LIVE:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_LIVE:
|
||||||
return <DeployNodeConfigFormVolcEngineLiveConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineLiveConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.VOLCENGINE_TOS:
|
case DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS:
|
||||||
return <DeployNodeConfigFormVolcEngineTOSConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormVolcEngineTOSConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.WANGSU_CDNPRO:
|
case DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO:
|
||||||
return <DeployNodeConfigFormWangsuCDNProConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormWangsuCDNProConfig {...nestedFormProps} />;
|
||||||
case DEPLOY_PROVIDERS.WEBHOOK:
|
case DEPLOYMENT_PROVIDERS.WEBHOOK:
|
||||||
return <DeployNodeConfigFormWebhookConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormWebhookConfig {...nestedFormProps} />;
|
||||||
}
|
}
|
||||||
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
|
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
|
||||||
@ -322,8 +322,6 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleProviderSelect = (value?: string | undefined) => {
|
const handleProviderSelect = (value?: string | undefined) => {
|
||||||
if (fieldProvider === value) return;
|
|
||||||
|
|
||||||
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
|
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
|
||||||
if (initialValues?.provider === value) {
|
if (initialValues?.provider === value) {
|
||||||
formInst.resetFields();
|
formInst.resetFields();
|
||||||
@ -339,7 +337,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
}
|
}
|
||||||
formInst.setFieldsValue(newValues);
|
formInst.setFieldsValue(newValues);
|
||||||
|
|
||||||
if (deployProvidersMap.get(fieldProvider)?.provider !== deployProvidersMap.get(value!)?.provider) {
|
if (deploymentProvidersMap.get(fieldProvider)?.provider !== deploymentProvidersMap.get(value!)?.provider) {
|
||||||
formInst.setFieldValue("providerAccessId", undefined);
|
formInst.setFieldValue("providerAccessId", undefined);
|
||||||
onValuesChange?.(formInst.getFieldsValue(true));
|
onValuesChange?.(formInst.getFieldsValue(true));
|
||||||
}
|
}
|
||||||
@ -384,10 +382,10 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
|
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
|
||||||
<Show
|
<Show
|
||||||
when={!!fieldProvider}
|
when={!!fieldProvider}
|
||||||
fallback={<DeployProviderPicker autoFocus placeholder={t("workflow_node.deploy.search.provider.placeholder")} onSelect={handleProviderPick} />}
|
fallback={<DeploymentProviderPicker autoFocus placeholder={t("workflow_node.deploy.search.provider.placeholder")} onSelect={handleProviderPick} />}
|
||||||
>
|
>
|
||||||
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
|
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
|
||||||
<DeployProviderSelect
|
<DeploymentProviderSelect
|
||||||
allowClear
|
allowClear
|
||||||
disabled={!!initialValues?.provider}
|
disabled={!!initialValues?.provider}
|
||||||
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
|
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
|
||||||
@ -410,8 +408,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<AccessEditModal
|
<AccessEditModal
|
||||||
data={{ provider: deployProvidersMap.get(fieldProvider!)?.provider }}
|
data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
|
||||||
range="both-dns-hosting"
|
|
||||||
scene="add"
|
scene="add"
|
||||||
trigger={
|
trigger={
|
||||||
<Button size="small" type="link">
|
<Button size="small" type="link">
|
||||||
@ -419,6 +416,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
<PlusOutlinedIcon className="text-xs" />
|
<PlusOutlinedIcon className="text-xs" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
usage="both-dns-hosting"
|
||||||
afterSubmit={(record) => {
|
afterSubmit={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
|
if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
|
||||||
@ -432,9 +430,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
<Form.Item name="providerAccessId" rules={[formRule]}>
|
<Form.Item name="providerAccessId" rules={[formRule]}>
|
||||||
<AccessSelect
|
<AccessSelect
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
if (fieldProvider) {
|
if (record.reserve) return false;
|
||||||
return deployProvidersMap.get(fieldProvider)?.provider === record.provider;
|
if (fieldProvider) return deploymentProvidersMap.get(fieldProvider)?.provider === record.provider;
|
||||||
}
|
|
||||||
|
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
return !!provider?.usages?.includes(ACCESS_USAGES.HOSTING);
|
return !!provider?.usages?.includes(ACCESS_USAGES.HOSTING);
|
||||||
@ -444,7 +441,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Show when={fieldProvider === DEPLOY_PROVIDERS.LOCAL}>
|
<Show when={fieldProvider === DEPLOYMENT_PROVIDERS.LOCAL}>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Alert
|
<Alert
|
||||||
type="info"
|
type="info"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user