feat: deprecate old notification module and introduce new notifier module

This commit is contained in:
Fu Diwei 2025-04-24 20:27:20 +08:00
parent 2d17501072
commit 7478dd7f47
27 changed files with 692 additions and 265 deletions

View File

@ -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")

View File

@ -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")
@ -71,18 +72,18 @@ type ApplyCAProviderType string
/* /*
申请证书 CA 提供商常量值 申请证书 CA 提供商常量值
始终等于授权提供商类型 短横线前的部分始终等于授权提供商类型
注意如果追加新的常量值请保持以 ASCII 排序 注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order. NOTICE: If you add new constant, please keep ASCII order.
*/ */
const ( const (
ApplyCAProviderTypeBuypass = ApplyCAProviderType(string(AccessProviderTypeBuypass)) ApplyCAProviderTypeBuypass = ApplyCAProviderType(AccessProviderTypeBuypass)
ApplyCAProviderTypeGoogleTrustServices = ApplyCAProviderType(string(AccessProviderTypeGoogleTrustServices)) ApplyCAProviderTypeGoogleTrustServices = ApplyCAProviderType(AccessProviderTypeGoogleTrustServices)
ApplyCAProviderTypeLetsEncrypt = ApplyCAProviderType(string(AccessProviderTypeLetsEncrypt)) ApplyCAProviderTypeLetsEncrypt = ApplyCAProviderType(AccessProviderTypeLetsEncrypt)
ApplyCAProviderTypeLetsEncryptStaging = ApplyCAProviderType(string(AccessProviderTypeLetsEncryptStaging)) ApplyCAProviderTypeLetsEncryptStaging = ApplyCAProviderType(AccessProviderTypeLetsEncryptStaging)
ApplyCAProviderTypeSSLCom = ApplyCAProviderType(string(AccessProviderTypeSSLCOM)) ApplyCAProviderTypeSSLCom = ApplyCAProviderType(AccessProviderTypeSSLCOM)
ApplyCAProviderTypeZeroSSL = ApplyCAProviderType(string(AccessProviderTypeZeroSSL)) ApplyCAProviderTypeZeroSSL = ApplyCAProviderType(AccessProviderTypeZeroSSL)
) )
type ApplyDNSProviderType string type ApplyDNSProviderType string
@ -95,43 +96,43 @@ type ApplyDNSProviderType string
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") ApplyDNSProviderTypeACMEHttpReq = ApplyDNSProviderType(AccessProviderTypeACMEHttpReq)
ApplyDNSProviderTypeAliyun = ApplyDNSProviderType("aliyun") // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS] ApplyDNSProviderTypeAliyun = ApplyDNSProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS]
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns") ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType(AccessProviderTypeAliyun + "-dns")
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53] ApplyDNSProviderTypeAWS = ApplyDNSProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53") ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType(AccessProviderTypeAWS + "-route53")
ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure] ApplyDNSProviderTypeAzure = ApplyDNSProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns") ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType(AccessProviderTypeAzure + "-dns")
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS] ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns") ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType(AccessProviderTypeBaiduCloud + "-dns")
ApplyDNSProviderTypeBunny = ApplyDNSProviderType("bunny") ApplyDNSProviderTypeBunny = ApplyDNSProviderType(AccessProviderTypeBunny)
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare") ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType(AccessProviderTypeCloudflare)
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns") ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType(AccessProviderTypeClouDNS)
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud") ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType(AccessProviderTypeCMCCCloud)
ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType("desec") ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType(AccessProviderTypeDeSEC)
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla") ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType(AccessProviderTypeDNSLA)
ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType("dynv6") ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType(AccessProviderTypeDynv6)
ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore") ApplyDNSProviderTypeGcore = ApplyDNSProviderType(AccessProviderTypeGcore)
ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname") ApplyDNSProviderTypeGname = ApplyDNSProviderType(AccessProviderTypeGname)
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy") ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType(AccessProviderTypeGoDaddy)
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS] ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns") ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType(AccessProviderTypeHuaweiCloud + "-dns")
ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS] ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS]
ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns") ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType(AccessProviderTypeJDCloud + "-dns")
ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType("namecheap") ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType(AccessProviderTypeNamecheap)
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom") ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType(AccessProviderTypeNameDotCom)
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo") ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType(AccessProviderTypeNameSilo)
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1") ApplyDNSProviderTypeNS1 = ApplyDNSProviderType(AccessProviderTypeNS1)
ApplyDNSProviderTypePorkbun = ApplyDNSProviderType("porkbun") ApplyDNSProviderTypePorkbun = ApplyDNSProviderType(AccessProviderTypePorkbun)
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns") ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType(AccessProviderTypePowerDNS)
ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun") ApplyDNSProviderTypeRainYun = ApplyDNSProviderType(AccessProviderTypeRainYun)
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS] ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns") ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType(AccessProviderTypeTencentCloud + "-dns")
ApplyDNSProviderTypeTencentCloudEO = ApplyDNSProviderType("tencentcloud-eo") ApplyDNSProviderTypeTencentCloudEO = ApplyDNSProviderType(AccessProviderTypeTencentCloud + "-eo")
ApplyDNSProviderTypeVercel = ApplyDNSProviderType("vercel") ApplyDNSProviderTypeVercel = ApplyDNSProviderType(AccessProviderTypeVercel)
ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS] ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS]
ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns") ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType(AccessProviderTypeVolcEngine + "-dns")
ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn") ApplyDNSProviderTypeWestcn = ApplyDNSProviderType(AccessProviderTypeWestcn)
) )
type DeployProviderType string type DeployProviderType string
@ -144,78 +145,91 @@ 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") DeployProviderType1PanelConsole = DeployProviderType(AccessProviderType1Panel + "-console")
DeployProviderType1PanelSite = DeployProviderType("1panel-site") DeployProviderType1PanelSite = DeployProviderType(AccessProviderType1Panel + "-site")
DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb") DeployProviderTypeAliyunALB = DeployProviderType(AccessProviderTypeAliyun + "-alb")
DeployProviderTypeAliyunAPIGW = DeployProviderType("aliyun-apigw") DeployProviderTypeAliyunAPIGW = DeployProviderType(AccessProviderTypeAliyun + "-apigw")
DeployProviderTypeAliyunCAS = DeployProviderType("aliyun-cas") DeployProviderTypeAliyunCAS = DeployProviderType(AccessProviderTypeAliyun + "-cas")
DeployProviderTypeAliyunCASDeploy = DeployProviderType("aliyun-casdeploy") DeployProviderTypeAliyunCASDeploy = DeployProviderType(AccessProviderTypeAliyun + "-casdeploy")
DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn") DeployProviderTypeAliyunCDN = DeployProviderType(AccessProviderTypeAliyun + "-cdn")
DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb") DeployProviderTypeAliyunCLB = DeployProviderType(AccessProviderTypeAliyun + "-clb")
DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn") DeployProviderTypeAliyunDCDN = DeployProviderType(AccessProviderTypeAliyun + "-dcdn")
DeployProviderTypeAliyunESA = DeployProviderType("aliyun-esa") DeployProviderTypeAliyunESA = DeployProviderType(AccessProviderTypeAliyun + "-esa")
DeployProviderTypeAliyunFC = DeployProviderType("aliyun-fc") DeployProviderTypeAliyunFC = DeployProviderType(AccessProviderTypeAliyun + "-fc")
DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live") DeployProviderTypeAliyunLive = DeployProviderType(AccessProviderTypeAliyun + "-live")
DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb") DeployProviderTypeAliyunNLB = DeployProviderType(AccessProviderTypeAliyun + "-nlb")
DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss") DeployProviderTypeAliyunOSS = DeployProviderType(AccessProviderTypeAliyun + "-oss")
DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod") DeployProviderTypeAliyunVOD = DeployProviderType(AccessProviderTypeAliyun + "-vod")
DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf") DeployProviderTypeAliyunWAF = DeployProviderType(AccessProviderTypeAliyun + "-waf")
DeployProviderTypeAWSACM = DeployProviderType("aws-acm") DeployProviderTypeAWSACM = DeployProviderType(AccessProviderTypeAWS + "-acm")
DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront") DeployProviderTypeAWSCloudFront = DeployProviderType(AccessProviderTypeAWS + "-cloudfront")
DeployProviderTypeAzureKeyVault = DeployProviderType("azure-keyvault") DeployProviderTypeAzureKeyVault = DeployProviderType(AccessProviderTypeAzure + "-keyvault")
DeployProviderTypeBaiduCloudAppBLB = DeployProviderType("baiducloud-appblb") DeployProviderTypeBaiduCloudAppBLB = DeployProviderType(AccessProviderTypeBaiduCloud + "-appblb")
DeployProviderTypeBaiduCloudBLB = DeployProviderType("baiducloud-blb") DeployProviderTypeBaiduCloudBLB = DeployProviderType(AccessProviderTypeBaiduCloud + "-blb")
DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") DeployProviderTypeBaiduCloudCDN = DeployProviderType(AccessProviderTypeBaiduCloud + "-cdn")
DeployProviderTypeBaiduCloudCert = DeployProviderType("baiducloud-cert") DeployProviderTypeBaiduCloudCert = DeployProviderType(AccessProviderTypeBaiduCloud + "-cert")
DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn") DeployProviderTypeBaishanCDN = DeployProviderType(AccessProviderTypeBaishan + "-cdn")
DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console") DeployProviderTypeBaotaPanelConsole = DeployProviderType(AccessProviderTypeBaotaPanel + "-console")
DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site") DeployProviderTypeBaotaPanelSite = DeployProviderType(AccessProviderTypeBaotaPanel + "-site")
DeployProviderTypeBunnyCDN = DeployProviderType("bunny-cdn") DeployProviderTypeBunnyCDN = DeployProviderType(AccessProviderTypeBunny + "-cdn")
DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") DeployProviderTypeBytePlusCDN = DeployProviderType(AccessProviderTypeBytePlus + "-cdn")
DeployProviderTypeCacheFly = DeployProviderType("cachefly") DeployProviderTypeCacheFly = DeployProviderType(AccessProviderTypeCacheFly)
DeployProviderTypeCdnfly = DeployProviderType("cdnfly") DeployProviderTypeCdnfly = DeployProviderType(AccessProviderTypeCdnfly)
DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") DeployProviderTypeDogeCloudCDN = DeployProviderType(AccessProviderTypeDogeCloud + "-cdn")
DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications") DeployProviderTypeEdgioApplications = DeployProviderType(AccessProviderTypeEdgio + "-applications")
DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn") DeployProviderTypeGcoreCDN = DeployProviderType(AccessProviderTypeGcore + "-cdn")
DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") DeployProviderTypeHuaweiCloudCDN = DeployProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") DeployProviderTypeHuaweiCloudELB = DeployProviderType(AccessProviderTypeHuaweiCloud + "-elb")
DeployProviderTypeHuaweiCloudSCM = DeployProviderType("huaweicloud-scm") DeployProviderTypeHuaweiCloudSCM = DeployProviderType(AccessProviderTypeHuaweiCloud + "-scm")
DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf") DeployProviderTypeHuaweiCloudWAF = DeployProviderType(AccessProviderTypeHuaweiCloud + "-waf")
DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb") DeployProviderTypeJDCloudALB = DeployProviderType(AccessProviderTypeJDCloud + "-alb")
DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn") DeployProviderTypeJDCloudCDN = DeployProviderType(AccessProviderTypeJDCloud + "-cdn")
DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live") DeployProviderTypeJDCloudLive = DeployProviderType(AccessProviderTypeJDCloud + "-live")
DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod") DeployProviderTypeJDCloudVOD = DeployProviderType(AccessProviderTypeJDCloud + "-vod")
DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") DeployProviderTypeKubernetesSecret = DeployProviderType(AccessProviderTypeKubernetes + "-secret")
DeployProviderTypeLocal = DeployProviderType("local") DeployProviderTypeLocal = DeployProviderType(AccessProviderTypeLocal)
DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") DeployProviderTypeQiniuCDN = DeployProviderType(AccessProviderTypeQiniu + "-cdn")
DeployProviderTypeQiniuKodo = DeployProviderType("qiniu-kodo") DeployProviderTypeQiniuKodo = DeployProviderType(AccessProviderTypeQiniu + "-kodo")
DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili") DeployProviderTypeQiniuPili = DeployProviderType(AccessProviderTypeQiniu + "-pili")
DeployProviderTypeRainYunRCDN = DeployProviderType("rainyun-rcdn") DeployProviderTypeRainYunRCDN = DeployProviderType(AccessProviderTypeRainYun + "-rcdn")
DeployProviderTypeSafeLine = DeployProviderType("safeline") DeployProviderTypeSafeLine = DeployProviderType(AccessProviderTypeSafeLine)
DeployProviderTypeSSH = DeployProviderType("ssh") DeployProviderTypeSSH = DeployProviderType(AccessProviderTypeSSH)
DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn") DeployProviderTypeTencentCloudCDN = DeployProviderType(AccessProviderTypeTencentCloud + "-cdn")
DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb") DeployProviderTypeTencentCloudCLB = DeployProviderType(AccessProviderTypeTencentCloud + "-clb")
DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos") DeployProviderTypeTencentCloudCOS = DeployProviderType(AccessProviderTypeTencentCloud + "-cos")
DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css") DeployProviderTypeTencentCloudCSS = DeployProviderType(AccessProviderTypeTencentCloud + "-css")
DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") DeployProviderTypeTencentCloudECDN = DeployProviderType(AccessProviderTypeTencentCloud + "-ecdn")
DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") DeployProviderTypeTencentCloudEO = DeployProviderType(AccessProviderTypeTencentCloud + "-eo")
DeployProviderTypeTencentCloudSCF = DeployProviderType("tencentcloud-scf") DeployProviderTypeTencentCloudSCF = DeployProviderType(AccessProviderTypeTencentCloud + "-scf")
DeployProviderTypeTencentCloudSSL = DeployProviderType("tencentcloud-ssl") DeployProviderTypeTencentCloudSSL = DeployProviderType(AccessProviderTypeTencentCloud + "-ssl")
DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy") DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType(AccessProviderTypeTencentCloud + "-ssldeploy")
DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod") DeployProviderTypeTencentCloudVOD = DeployProviderType(AccessProviderTypeTencentCloud + "-vod")
DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf") DeployProviderTypeTencentCloudWAF = DeployProviderType(AccessProviderTypeTencentCloud + "-waf")
DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") DeployProviderTypeUCloudUCDN = DeployProviderType(AccessProviderTypeUCloud + "-ucdn")
DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3") DeployProviderTypeUCloudUS3 = DeployProviderType(AccessProviderTypeUCloud + "-us3")
DeployProviderTypeUpyunCDN = DeployProviderType("upyun-cdn") DeployProviderTypeUpyunCDN = DeployProviderType(AccessProviderTypeUpyun + "-cdn")
DeployProviderTypeUpyunFile = DeployProviderType("upyun-file") DeployProviderTypeUpyunFile = DeployProviderType(AccessProviderTypeUpyun + "-file")
DeployProviderTypeVolcEngineALB = DeployProviderType("volcengine-alb") DeployProviderTypeVolcEngineALB = DeployProviderType(AccessProviderTypeVolcEngine + "-alb")
DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") DeployProviderTypeVolcEngineCDN = DeployProviderType(AccessProviderTypeVolcEngine + "-cdn")
DeployProviderTypeVolcEngineCertCenter = DeployProviderType("volcengine-certcenter") DeployProviderTypeVolcEngineCertCenter = DeployProviderType(AccessProviderTypeVolcEngine + "-certcenter")
DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") DeployProviderTypeVolcEngineCLB = DeployProviderType(AccessProviderTypeVolcEngine + "-clb")
DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") DeployProviderTypeVolcEngineDCDN = DeployProviderType(AccessProviderTypeVolcEngine + "-dcdn")
DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex") DeployProviderTypeVolcEngineImageX = DeployProviderType(AccessProviderTypeVolcEngine + "-imagex")
DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live") DeployProviderTypeVolcEngineLive = DeployProviderType(AccessProviderTypeVolcEngine + "-live")
DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos") DeployProviderTypeVolcEngineTOS = DeployProviderType(AccessProviderTypeVolcEngine + "-tos")
DeployProviderTypeWangsuCDNPro = DeployProviderType("wangsu-cdnpro") DeployProviderTypeWangsuCDNPro = DeployProviderType(AccessProviderTypeWangsu + "-cdnpro")
DeployProviderTypeWebhook = DeployProviderType("webhook") DeployProviderTypeWebhook = DeployProviderType(AccessProviderTypeWebhook)
)
type NotifyProviderType string
/*
消息通知提供商常量值
短横线前的部分始终等于授权提供商类型
注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
NotifyProviderTypeWebhook = NotifyProviderType(AccessProviderTypeWebhook)
) )

View File

@ -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 {

View File

@ -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,7 +95,10 @@ type WorkflowNodeConfigForDeploy struct {
} }
type WorkflowNodeConfigForNotify struct { type WorkflowNodeConfigForNotify struct {
Channel string `json:"channel"` // 通知渠道 Channel string `json:"channel,omitempty"` // Deprecated: v0.4.x 将废弃
Provider string `json:"provider"` // 通知提供商
ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
Subject string `json:"subject"` // 通知主题 Subject string `json:"subject"` // 通知主题
Message string `json:"message"` // 通知内容 Message string `json:"message"` // 通知内容
} }
@ -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.GetMap(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.GetMap(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,7 +141,7 @@ 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.GetMap(n.Config, "providerConfig"),
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"), SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
} }
} }
@ -146,6 +149,9 @@ func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
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"),
Provider: maputil.GetString(n.Config, "provider"),
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
ProviderConfig: maputil.GetMap(n.Config, "providerConfig"),
Subject: maputil.GetString(n.Config, "subject"), Subject: maputil.GetString(n.Config, "subject"),
Message: maputil.GetString(n.Config, "message"), Message: maputil.GetString(n.Config, "message"),
} }

View 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 := &notifierProviderOptions{
Provider: domain.NotifyProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderNotifyConfig: 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 &notifierImpl{
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
}

View File

@ -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
} }

View File

@ -5,102 +5,28 @@ import (
"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"
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" 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" 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.NotifyProviderType
ProviderAccessConfig map[string]any
ProviderNotifyConfig 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.NotifyProviderTypeWebhook:
return pBark.NewNotifier(&pBark.NotifierConfig{
DeviceKey: maputil.GetString(channelConfig, "deviceKey"),
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
})
case domain.NotifyChannelTypeDingTalk:
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
AccessToken: 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{
ApiToken: maputil.GetString(channelConfig, "apiToken"),
ChatId: maputil.GetInt64(channelConfig, "chatId"),
})
case domain.NotifyChannelTypeWebhook:
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
Url: maputil.GetString(channelConfig, "url"), Url: maputil.GetString(options.ProviderAccessConfig, "url"),
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"), AllowInsecureConnections: maputil.GetBool(options.ProviderAccessConfig, "allowInsecureConnections"),
})
case domain.NotifyChannelTypeWeCom:
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
}) })
} }
return nil, fmt.Errorf("unsupported notifier channel '%s'", channelConfig) return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider)
} }

View 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{
AccessToken: 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{
ApiToken: maputil.GetString(channelConfig, "apiToken"),
ChatId: maputil.GetInt64(channelConfig, "chatId"),
})
case domain.NotifyChannelTypeWebhook:
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
Url: 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)
}

View File

@ -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 {

View File

@ -207,7 +207,7 @@ 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 GetMap(dict map[string]any, key string) map[string]any {
if dict == nil { if dict == nil {
return make(map[string]any) return make(map[string]any)
} }

View File

@ -49,14 +49,17 @@ func (n *applyNode) Process(ctx context.Context) error {
} }
// 初始化申请器 // 初始化申请器
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

View File

@ -63,17 +63,18 @@ func (n *deployNode) Process(ctx context.Context) error {
} }
// 初始化部署器 // 初始化部署器
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

View File

@ -30,6 +30,11 @@ func (n *notifyNode) Process(ctx context.Context) error {
nodeConfig := n.node.GetConfigForNotify() nodeConfig := n.node.GetConfigForNotify()
if nodeConfig.Provider == "" {
// 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") settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
if err != nil { if err != nil {
@ -49,6 +54,26 @@ func (n *notifyNode) Process(ctx context.Context) error {
} }
n.logger.Info("notify completed") 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 {
n.logger.Warn("failed to create notifier provider")
return err
}
// 推送通知
if err := deployer.Notify(ctx); err != nil {
n.logger.Warn("failed to notify")
return err
}
return nil return nil
} }

View 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 NotifyProvider, notifyProvidersMap } from "@/domain/provider";
export type NotifyProviderSelectProps = Omit<
SelectProps,
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
> & {
filter?: (record: NotifyProvider) => boolean;
};
const NotifyProviderSelect = ({ filter, ...props }: NotifyProviderSelectProps) => {
const { t } = useTranslation();
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: NotifyProvider }>>([]);
useEffect(() => {
const allItems = Array.from(notifyProvidersMap.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 = notifyProvidersMap.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(NotifyProviderSelect);

View File

@ -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)
); );

View File

@ -1,8 +1,9 @@
import { memo, useMemo, useRef, useState } from "react"; import { memo, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Flex, Typography } from "antd"; import { Avatar, Flex, Typography } from "antd";
import { produce } from "immer"; import { produce } from "immer";
import { notifyProvidersMap } from "@/domain/provider";
import { notifyChannelsMap } from "@/domain/settings"; import { notifyChannelsMap } from "@/domain/settings";
import { type WorkflowNodeConfigForNotify, WorkflowNodeType } from "@/domain/workflow"; import { type WorkflowNodeConfigForNotify, WorkflowNodeType } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks"; import { useZustandShallowSelector } from "@/hooks";
@ -39,9 +40,11 @@ const NotifyNode = ({ node, disabled }: NotifyNodeProps) => {
const config = (node.config as WorkflowNodeConfigForNotify) ?? {}; const config = (node.config as WorkflowNodeConfigForNotify) ?? {};
const channel = notifyChannelsMap.get(config.channel as string); const channel = notifyChannelsMap.get(config.channel as string);
const provider = notifyProvidersMap.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}>
<Typography.Text className="flex-1 truncate">{t(channel?.name ?? " ")}</Typography.Text> <Avatar src={provider?.icon} size="small" />
<Typography.Text className="flex-1 truncate">{t(channel?.name ?? provider?.name ?? " ")}</Typography.Text>
<Typography.Text className="truncate" type="secondary"> <Typography.Text className="truncate" type="secondary">
{config.subject ?? ""} {config.subject ?? ""}
</Typography.Text> </Typography.Text>

View File

@ -1,14 +1,19 @@
import { forwardRef, memo, useEffect, useImperativeHandle } from "react"; import { forwardRef, memo, useEffect, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router"; import { Link } from "react-router";
import { RightOutlined as RightOutlinedIcon } from "@ant-design/icons"; import { PlusOutlined as PlusOutlinedIcon, RightOutlined as RightOutlinedIcon } from "@ant-design/icons";
import { Button, Form, type FormInstance, Input, Select } from "antd"; import { Alert, Button, Form, type FormInstance, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod"; import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod"; import { z } from "zod";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
import NotifyProviderSelect from "@/components/provider/NotifyProviderSelect";
import { ACCESS_USAGES, accessProvidersMap, notifyProvidersMap } from "@/domain/provider";
import { notifyChannelsMap } from "@/domain/settings"; import { notifyChannelsMap } from "@/domain/settings";
import { type WorkflowNodeConfigForNotify } from "@/domain/workflow"; import { type WorkflowNodeConfigForNotify } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks"; import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useAccessesStore } from "@/stores/access";
import { useNotifyChannelsStore } from "@/stores/notify"; import { useNotifyChannelsStore } from "@/stores/notify";
type NotifyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForNotify>; type NotifyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForNotify>;
@ -35,6 +40,8 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
({ className, style, disabled, initialValues, onValuesChange }, ref) => { ({ className, style, disabled, initialValues, onValuesChange }, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { accesses } = useAccessesStore(useZustandShallowSelector("accesses"));
const { const {
channels, channels,
loadedAtOnce: channelsLoadedAtOnce, loadedAtOnce: channelsLoadedAtOnce,
@ -53,7 +60,11 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
.string({ message: t("workflow_node.notify.form.message.placeholder") }) .string({ message: t("workflow_node.notify.form.message.placeholder") })
.min(1, t("workflow_node.notify.form.message.placeholder")) .min(1, t("workflow_node.notify.form.message.placeholder"))
.max(1000, t("common.errmsg.string_max", { max: 1000 })), .max(1000, t("common.errmsg.string_max", { max: 1000 })),
channel: z.string({ message: t("workflow_node.notify.form.channel.placeholder") }).min(1, t("workflow_node.notify.form.channel.placeholder")), channel: z.string().nullish(),
provider: z.string({ message: t("workflow_node.notify.form.provider.placeholder") }).nonempty(t("workflow_node.notify.form.provider.placeholder")),
providerAccessId: z
.string({ message: t("workflow_node.notify.form.provider_access.placeholder") })
.nonempty(t("workflow_node.notify.form.provider_access.placeholder")),
}); });
const formRule = createSchemaFieldRule(formSchema); const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({ const { form: formInst, formProps } = useAntdForm({
@ -61,6 +72,49 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
initialValues: initialValues ?? initFormModel(), initialValues: initialValues ?? initFormModel(),
}); });
const fieldProvider = Form.useWatch<string>("provider", { form: formInst, preserve: true });
const fieldProviderAccessId = Form.useWatch<string>("providerAccessId", formInst);
const [showProvider, setShowProvider] = useState(false);
useEffect(() => {
// 通常情况下每个授权信息只对应一个消息通知提供商,此时无需显示消息通知提供商字段;
// 如果对应多个,则显示。
if (fieldProviderAccessId) {
const access = accesses.find((e) => e.id === fieldProviderAccessId);
const providers = Array.from(notifyProvidersMap.values()).filter((e) => e.provider === access?.provider);
setShowProvider(providers.length > 1);
} else {
setShowProvider(false);
}
}, [accesses, fieldProviderAccessId]);
const handleProviderSelect = (value: string) => {
if (fieldProvider === value) return;
// 切换消息通知提供商时联动授权信息
if (initialValues?.provider === value) {
formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId);
onValuesChange?.(formInst.getFieldsValue(true));
} else {
if (notifyProvidersMap.get(fieldProvider)?.provider !== notifyProvidersMap.get(value)?.provider) {
formInst.setFieldValue("providerAccessId", undefined);
onValuesChange?.(formInst.getFieldsValue(true));
}
}
};
const handleProviderAccessSelect = (value: string) => {
if (fieldProviderAccessId === value) return;
// 切换授权信息时联动消息通知提供商
const access = accesses.find((access) => access.id === value);
const provider = Array.from(notifyProvidersMap.values()).find((provider) => provider.provider === access?.provider);
if (fieldProvider !== provider?.type) {
formInst.setFieldValue("provider", provider?.type);
onValuesChange?.(formInst.getFieldsValue(true));
}
};
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values as NotifyNodeConfigFormFieldValues); onValuesChange?.(values as NotifyNodeConfigFormFieldValues);
}; };
@ -92,7 +146,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
<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.notify.form.channel.label")}</div> <div className="max-w-full grow truncate line-through">{t("workflow_node.notify.form.channel.label")}</div>
<div className="text-right"> <div className="text-right">
<Link className="ant-typography" to="/settings/notification" target="_blank"> <Link className="ant-typography" to="/settings/notification" target="_blank">
<Button size="small" type="link"> <Button size="small" type="link">
@ -116,6 +170,60 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
/> />
</Form.Item> </Form.Item>
</Form.Item> </Form.Item>
<Form.Item name="provider" label={t("workflow_node.notify.form.provider.label")} hidden={!showProvider} rules={[formRule]}>
<NotifyProviderSelect
disabled={!showProvider}
filter={(record) => {
if (fieldProviderAccessId) {
return accesses.find((e) => e.id === fieldProviderAccessId)?.provider === record.provider;
}
return true;
}}
placeholder={t("workflow_node.notify.form.provider.placeholder")}
showSearch
onSelect={handleProviderSelect}
/>
</Form.Item>
<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("workflow_node.notify.form.provider_access.label")}</span>
</div>
<div className="text-right">
<AccessEditModal
range="notify-only"
scene="add"
trigger={
<Button size="small" type="link">
{t("workflow_node.notify.form.provider_access.button")}
<PlusOutlinedIcon className="text-xs" />
</Button>
}
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION)) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect
filter={(record) => {
const provider = accessProvidersMap.get(record.provider);
return !!provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION);
}}
placeholder={t("workflow_node.notify.form.provider_access.placeholder")}
onChange={handleProviderAccessSelect}
/>
</Form.Item>
</Form.Item>
</Form> </Form>
); );
} }

View File

@ -500,3 +500,38 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv
]) ])
); );
// #endregion // #endregion
// #region NotifyProvider
/*
ASCII
NOTICE: If you add new constant, please keep ASCII order.
*/
export const NOTIFY_PROVIDERS = Object.freeze({
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
} as const);
export type NotifyProviderType = (typeof APPLY_CA_PROVIDERS)[keyof typeof APPLY_CA_PROVIDERS];
export type NotifyProvider = {
type: NotifyProviderType;
name: string;
icon: string;
provider: AccessProviderType;
};
export const notifyProvidersMap: Map<NotifyProvider["type"] | string, NotifyProvider> = new Map(
/*
NOTICE: The following order determines the order displayed at the frontend.
*/
[[NOTIFY_PROVIDERS.WEBHOOK]].map(([type]) => [
type,
{
type: type as ApplyCAProviderType,
name: accessProvidersMap.get(type.split("-")[0])!.name,
icon: accessProvidersMap.get(type.split("-")[0])!.icon,
provider: type.split("-")[0] as AccessProviderType,
},
])
);
// #endregion

View File

@ -3,6 +3,9 @@ import { type ApplyCAProviderType } from "./provider";
export const SETTINGS_NAMES = Object.freeze({ export const SETTINGS_NAMES = Object.freeze({
EMAILS: "emails", EMAILS: "emails",
NOTIFY_TEMPLATES: "notifyTemplates", NOTIFY_TEMPLATES: "notifyTemplates",
/**
* @deprecated
*/
NOTIFY_CHANNELS: "notifyChannels", NOTIFY_CHANNELS: "notifyChannels",
SSL_PROVIDER: "sslProvider", SSL_PROVIDER: "sslProvider",
PERSISTENCE: "persistence", PERSISTENCE: "persistence",
@ -38,6 +41,9 @@ export const defaultNotifyTemplate: NotifyTemplate = {
// #endregion // #endregion
// #region Settings: NotifyChannels // #region Settings: NotifyChannels
/**
* @deprecated
*/
export const NOTIFY_CHANNELS = Object.freeze({ export const NOTIFY_CHANNELS = Object.freeze({
BARK: "bark", BARK: "bark",
DINGTALK: "dingtalk", DINGTALK: "dingtalk",
@ -53,8 +59,14 @@ export const NOTIFY_CHANNELS = Object.freeze({
WECOM: "wecom", WECOM: "wecom",
} as const); } as const);
/**
* @deprecated
*/
export type NotifyChannels = (typeof NOTIFY_CHANNELS)[keyof typeof NOTIFY_CHANNELS]; export type NotifyChannels = (typeof NOTIFY_CHANNELS)[keyof typeof NOTIFY_CHANNELS];
/**
* @deprecated
*/
export type NotifyChannelsSettingsContent = { export type NotifyChannelsSettingsContent = {
/* /*
ASCII ASCII
@ -116,7 +128,7 @@ export type MattermostNotifyChannelConfig = {
username: string; username: string;
password: string; password: string;
enabled?: boolean; enabled?: boolean;
} };
export type PushoverNotifyChannelConfig = { export type PushoverNotifyChannelConfig = {
token: string; token: string;
@ -155,6 +167,9 @@ export type NotifyChannel = {
name: string; name: string;
}; };
/**
* @deprecated
*/
export const notifyChannelsMap: Map<NotifyChannel["type"], NotifyChannel> = new Map( export const notifyChannelsMap: Map<NotifyChannel["type"], NotifyChannel> = new Map(
[ [
[NOTIFY_CHANNELS.EMAIL, "common.notifier.email"], [NOTIFY_CHANNELS.EMAIL, "common.notifier.email"],

View File

@ -154,9 +154,15 @@ export type WorkflowNodeConfigForDeploy = {
}; };
export type WorkflowNodeConfigForNotify = { export type WorkflowNodeConfigForNotify = {
channel: string;
subject: string; subject: string;
message: string; message: string;
/**
* @deprecated
*/
channel?: string;
provider: string;
providerAccessId: string;
providerConfig?: Record<string, unknown>;
}; };
export type WorkflowNodeConfigForBranch = never; export type WorkflowNodeConfigForBranch = never;

View File

@ -712,9 +712,14 @@
"workflow_node.notify.form.subject.placeholder": "Please enter subject", "workflow_node.notify.form.subject.placeholder": "Please enter subject",
"workflow_node.notify.form.message.label": "Message", "workflow_node.notify.form.message.label": "Message",
"workflow_node.notify.form.message.placeholder": "Please enter message", "workflow_node.notify.form.message.placeholder": "Please enter message",
"workflow_node.notify.form.channel.label": "Channel", "workflow_node.notify.form.channel.label": "Channel (Deprecated)",
"workflow_node.notify.form.channel.placeholder": "Please select channel", "workflow_node.notify.form.channel.placeholder": "Please select channel",
"workflow_node.notify.form.channel.button": "Configure", "workflow_node.notify.form.channel.button": "Configure",
"workflow_node.notify.form.provider.label": "Notification channel",
"workflow_node.notify.form.provider.placeholder": "Please select notification channel",
"workflow_node.notify.form.provider_access.label": "Notification provider authorization",
"workflow_node.notify.form.provider_access.placeholder": "Please select an authorization of notification provider",
"workflow_node.notify.form.provider_access.button": "Create",
"workflow_node.end.label": "End", "workflow_node.end.label": "End",

View File

@ -711,9 +711,14 @@
"workflow_node.notify.form.subject.placeholder": "请输入通知主题", "workflow_node.notify.form.subject.placeholder": "请输入通知主题",
"workflow_node.notify.form.message.label": "通知内容", "workflow_node.notify.form.message.label": "通知内容",
"workflow_node.notify.form.message.placeholder": "请输入通知内容", "workflow_node.notify.form.message.placeholder": "请输入通知内容",
"workflow_node.notify.form.channel.label": "通知渠道", "workflow_node.notify.form.channel.label": "通知渠道(已废弃,请使用「通知渠道授权」字段)",
"workflow_node.notify.form.channel.placeholder": "请选择通知渠道", "workflow_node.notify.form.channel.placeholder": "请选择通知渠道",
"workflow_node.notify.form.channel.button": "去配置", "workflow_node.notify.form.channel.button": "设置",
"workflow_node.notify.form.provider.label": "通知渠道",
"workflow_node.notify.form.provider.placeholder": "请选择通知渠道",
"workflow_node.notify.form.provider_access.label": "通知渠道授权",
"workflow_node.notify.form.provider_access.placeholder": "请选择通知渠道授权",
"workflow_node.notify.form.provider_access.button": "新建",
"workflow_node.end.label": "结束", "workflow_node.end.label": "结束",

View File

@ -185,10 +185,12 @@ const AccessList = () => {
const handleTabChange = (key: string) => { const handleTabChange = (key: string) => {
setFilters((prev) => ({ ...prev, range: key })); setFilters((prev) => ({ ...prev, range: key }));
setPage(1);
}; };
const handleSearch = (value: string) => { const handleSearch = (value: string) => {
setFilters((prev) => ({ ...prev, keyword: value })); setFilters((prev) => ({ ...prev, keyword: value }));
setPage(1);
}; };
const handleReloadClick = () => { const handleReloadClick = () => {
@ -251,10 +253,10 @@ const AccessList = () => {
key: "ca-only", key: "ca-only",
label: t("access.props.range.ca_only"), label: t("access.props.range.ca_only"),
}, },
// { {
// key: "notify-only", key: "notify-only",
// label: t("access.props.range.notify_only"), label: t("access.props.range.notify_only"),
// }, },
]} ]}
activeTabKey={filters["range"] as string} activeTabKey={filters["range"] as string}
onTabChange={(key) => handleTabChange(key)} onTabChange={(key) => handleTabChange(key)}

View File

@ -251,6 +251,7 @@ const CertificateList = () => {
const handleSearch = (value: string) => { const handleSearch = (value: string) => {
setFilters((prev) => ({ ...prev, keyword: value.trim() })); setFilters((prev) => ({ ...prev, keyword: value.trim() }));
setPage(1);
}; };
const handleReloadClick = () => { const handleReloadClick = () => {

View File

@ -1,11 +1,14 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Card, Divider } from "antd"; import { Alert, Card, Divider } from "antd";
import NotifyChannels from "@/components/notification/NotifyChannels"; import NotifyChannels from "@/components/notification/NotifyChannels";
import NotifyTemplate from "@/components/notification/NotifyTemplate"; import NotifyTemplate from "@/components/notification/NotifyTemplate";
import { useZustandShallowSelector } from "@/hooks"; import { useZustandShallowSelector } from "@/hooks";
import { useNotifyChannelsStore } from "@/stores/notify"; import { useNotifyChannelsStore } from "@/stores/notify";
/**
* @deprecated
*/
const SettingsNotification = () => { const SettingsNotification = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -22,6 +25,7 @@ const SettingsNotification = () => {
<Divider /> <Divider />
<Card className="shadow" styles={{ body: loadedAtOnce ? { padding: 0 } : {} }} title={t("settings.notification.channels.card.title")}> <Card className="shadow" styles={{ body: loadedAtOnce ? { padding: 0 } : {} }} title={t("settings.notification.channels.card.title")}>
<Alert type="warning" banner message="本页面相关功能即将在后续版本中废弃,请使用「授权管理」页面来管理通知渠道。" />
<NotifyChannels classNames={{ form: "md:max-w-[40rem]" }} /> <NotifyChannels classNames={{ form: "md:max-w-[40rem]" }} />
</Card> </Card>
</div> </div>

View File

@ -281,6 +281,7 @@ const WorkflowList = () => {
const handleSearch = (value: string) => { const handleSearch = (value: string) => {
setFilters((prev) => ({ ...prev, keyword: value.trim() })); setFilters((prev) => ({ ...prev, keyword: value.trim() }));
setPage(1);
}; };
const handleCreateClick = () => { const handleCreateClick = () => {

View File

@ -4,6 +4,9 @@ import { create } from "zustand";
import { type NotifyChannelsSettingsContent, SETTINGS_NAMES, type SettingsModel } from "@/domain/settings"; import { type NotifyChannelsSettingsContent, SETTINGS_NAMES, type SettingsModel } from "@/domain/settings";
import { get as getSettings, save as saveSettings } from "@/repository/settings"; import { get as getSettings, save as saveSettings } from "@/repository/settings";
/**
* @deprecated
*/
export interface NotifyChannelsState { export interface NotifyChannelsState {
channels: NotifyChannelsSettingsContent; channels: NotifyChannelsSettingsContent;
loading: boolean; loading: boolean;
@ -14,6 +17,9 @@ export interface NotifyChannelsState {
setChannels: (channels: NotifyChannelsSettingsContent) => Promise<void>; setChannels: (channels: NotifyChannelsSettingsContent) => Promise<void>;
} }
/**
* @deprecated
*/
export const useNotifyChannelsStore = create<NotifyChannelsState>((set, get) => { export const useNotifyChannelsStore = create<NotifyChannelsState>((set, get) => {
let fetcher: Promise<SettingsModel<NotifyChannelsSettingsContent>> | null = null; // 防止多次重复请求 let fetcher: Promise<SettingsModel<NotifyChannelsSettingsContent>> | null = null; // 防止多次重复请求
let settings: SettingsModel<NotifyChannelsSettingsContent>; // 记录当前设置的其他字段,保存回数据库时用 let settings: SettingsModel<NotifyChannelsSettingsContent>; // 记录当前设置的其他字段,保存回数据库时用