mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-06 06:24:54 +00:00
merge source
This commit is contained in:
@@ -19,12 +19,15 @@ import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
)
|
||||
|
||||
const (
|
||||
configTypeAliyun = "aliyun"
|
||||
configTypeTencent = "tencent"
|
||||
configTypeHuaweicloud = "huaweicloud"
|
||||
configTypeHuaweiCloud = "huaweicloud"
|
||||
configTypeAws = "aws"
|
||||
configTypeCloudflare = "cloudflare"
|
||||
configTypeNamesilo = "namesilo"
|
||||
@@ -128,7 +131,7 @@ func Get(record *models.Record) (Applicant, error) {
|
||||
return NewAliyun(option), nil
|
||||
case configTypeTencent:
|
||||
return NewTencent(option), nil
|
||||
case configTypeHuaweicloud:
|
||||
case configTypeHuaweiCloud:
|
||||
return NewHuaweiCloud(option), nil
|
||||
case configTypeAws:
|
||||
return NewAws(option), nil
|
||||
|
@@ -20,7 +20,7 @@ type AliyunCDNDeployer struct {
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunCdnDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
|
||||
func NewAliyunCDNDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
@@ -41,7 +41,7 @@ func NewAliyunCdnDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
|
||||
}
|
||||
|
||||
func (d *AliyunCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunCDNDeployer) GetInfo() []string {
|
||||
|
@@ -25,7 +25,7 @@ type AliyunESADeployer struct {
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunEsaDeployer(option *DeployerOption) (*AliyunESADeployer, error) {
|
||||
func NewAliyunESADeployer(option *DeployerOption) (*AliyunESADeployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
@@ -46,7 +46,7 @@ func NewAliyunEsaDeployer(option *DeployerOption) (*AliyunESADeployer, error) {
|
||||
}
|
||||
|
||||
func (d *AliyunESADeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunESADeployer) GetInfo() []string {
|
||||
|
@@ -16,7 +16,7 @@ type AliyunOSSDeployer struct {
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunOssDeployer(option *DeployerOption) (Deployer, error) {
|
||||
func NewAliyunOSSDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewAliyunOssDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) GetInfo() []string {
|
||||
|
@@ -15,15 +15,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
targetAliyunOSS = "aliyun-oss"
|
||||
targetAliyunCDN = "aliyun-cdn"
|
||||
targetAliyunESA = "aliyun-dcdn"
|
||||
targetTencentCDN = "tencent-cdn"
|
||||
targetQiniuCdn = "qiniu-cdn"
|
||||
targetLocal = "local"
|
||||
targetSSH = "ssh"
|
||||
targetWebhook = "webhook"
|
||||
targetK8sSecret = "k8s-secret"
|
||||
targetAliyunOSS = "aliyun-oss"
|
||||
targetAliyunCDN = "aliyun-cdn"
|
||||
targetAliyunESA = "aliyun-dcdn"
|
||||
targetTencentCDN = "tencent-cdn"
|
||||
targetHuaweiCloudCDN = "huaweicloud-cdn"
|
||||
targetQiniuCdn = "qiniu-cdn"
|
||||
targetLocal = "local"
|
||||
targetSSH = "ssh"
|
||||
targetWebhook = "webhook"
|
||||
targetK8sSecret = "k8s-secret"
|
||||
)
|
||||
|
||||
type DeployerOption struct {
|
||||
@@ -31,7 +32,7 @@ type DeployerOption struct {
|
||||
Domain string `json:"domain"`
|
||||
Product string `json:"product"`
|
||||
Access string `json:"access"`
|
||||
AceessRecord *models.Record `json:"-"`
|
||||
AccessRecord *models.Record `json:"-"`
|
||||
DeployConfig domain.DeployConfig `json:"deployConfig"`
|
||||
Certificate applicant.Certificate `json:"certificate"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
@@ -83,7 +84,7 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
||||
Domain: record.GetString("domain"),
|
||||
Product: getProduct(deployConfig.Type),
|
||||
Access: access.GetString("config"),
|
||||
AceessRecord: access,
|
||||
AccessRecord: access,
|
||||
DeployConfig: deployConfig,
|
||||
}
|
||||
if cert != nil {
|
||||
@@ -97,13 +98,15 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
||||
|
||||
switch deployConfig.Type {
|
||||
case targetAliyunOSS:
|
||||
return NewAliyunOssDeployer(option)
|
||||
return NewAliyunOSSDeployer(option)
|
||||
case targetAliyunCDN:
|
||||
return NewAliyunCdnDeployer(option)
|
||||
return NewAliyunCDNDeployer(option)
|
||||
case targetAliyunESA:
|
||||
return NewAliyunEsaDeployer(option)
|
||||
return NewAliyunESADeployer(option)
|
||||
case targetTencentCDN:
|
||||
return NewTencentCDNDeployer(option)
|
||||
case targetHuaweiCloudCDN:
|
||||
return NewHuaweiCloudCDNDeployer(option)
|
||||
case targetQiniuCdn:
|
||||
return NewQiniuCDNDeployer(option)
|
||||
case targetLocal:
|
||||
|
150
internal/deployer/huaweicloud_cdn.go
Normal file
150
internal/deployer/huaweicloud_cdn.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
|
||||
cdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
|
||||
cdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
|
||||
cdnRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/rand"
|
||||
)
|
||||
|
||||
type HuaweiCloudCDNDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &HuaweiCloudCDNDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudCDNDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
|
||||
access := &domain.HuaweiCloudAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := d.createClient(access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("HuaweiCloudCdnClient 创建成功", nil))
|
||||
|
||||
// 查询加速域名配置
|
||||
showDomainFullConfigReq := &cdnModel.ShowDomainFullConfigRequest{
|
||||
DomainName: getDeployString(d.option.DeployConfig, "domain"),
|
||||
}
|
||||
showDomainFullConfigResp, err := client.ShowDomainFullConfig(showDomainFullConfigReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到加速域名配置", showDomainFullConfigResp))
|
||||
|
||||
// 更新加速域名配置
|
||||
certName := fmt.Sprintf("%s-%s", d.option.DomainId, rand.RandStr(12))
|
||||
updateDomainMultiCertificatesReq := &cdnModel.UpdateDomainMultiCertificatesRequest{
|
||||
Body: &cdnModel.UpdateDomainMultiCertificatesRequestBody{
|
||||
Https: mergeHuaweiCloudCDNConfig(showDomainFullConfigResp.Configs, &cdnModel.UpdateDomainMultiCertificatesRequestBodyContent{
|
||||
DomainName: getDeployString(d.option.DeployConfig, "domain"),
|
||||
HttpsSwitch: 1,
|
||||
CertName: &certName,
|
||||
Certificate: &d.option.Certificate.Certificate,
|
||||
PrivateKey: &d.option.Certificate.PrivateKey,
|
||||
}),
|
||||
},
|
||||
}
|
||||
updateDomainMultiCertificatesResp, err := client.UpdateDomainMultiCertificates(updateDomainMultiCertificatesReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已更新加速域名配置", updateDomainMultiCertificatesResp))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudCDNDeployer) createClient(access *domain.HuaweiCloudAccess) (*cdn.CdnClient, error) {
|
||||
auth, err := global.NewCredentialsBuilder().
|
||||
WithAk(access.AccessKeyId).
|
||||
WithSk(access.SecretAccessKey).
|
||||
SafeBuild()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
region, err := cdnRegion.SafeValueOf(access.Region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hcClient, err := cdn.CdnClientBuilder().
|
||||
WithRegion(region).
|
||||
WithCredential(auth).
|
||||
SafeBuild()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := cdn.NewCdnClient(hcClient)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func mergeHuaweiCloudCDNConfig(src *cdnModel.ConfigsGetBody, dest *cdnModel.UpdateDomainMultiCertificatesRequestBodyContent) *cdnModel.UpdateDomainMultiCertificatesRequestBodyContent {
|
||||
if src == nil {
|
||||
return dest
|
||||
}
|
||||
|
||||
// 华为云 API 中不传的字段表示使用默认值、而非保留原值,因此这里需要把原配置中的参数重新赋值回去
|
||||
// 而且蛋疼的是查询接口返回的数据结构和更新接口传入的参数结构不一致,需要做很多转化
|
||||
// REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html
|
||||
// REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html
|
||||
|
||||
if *src.OriginProtocol == "follow" {
|
||||
accessOriginWay := int32(1)
|
||||
dest.AccessOriginWay = &accessOriginWay
|
||||
} else if *src.OriginProtocol == "http" {
|
||||
accessOriginWay := int32(2)
|
||||
dest.AccessOriginWay = &accessOriginWay
|
||||
} else if *src.OriginProtocol == "https" {
|
||||
accessOriginWay := int32(3)
|
||||
dest.AccessOriginWay = &accessOriginWay
|
||||
}
|
||||
|
||||
if src.ForceRedirect != nil {
|
||||
dest.ForceRedirectConfig = &cdnModel.ForceRedirect{}
|
||||
|
||||
if src.ForceRedirect.Status == "on" {
|
||||
dest.ForceRedirectConfig.Switch = 1
|
||||
dest.ForceRedirectConfig.RedirectType = src.ForceRedirect.Type
|
||||
} else {
|
||||
dest.ForceRedirectConfig.Switch = 0
|
||||
}
|
||||
}
|
||||
|
||||
if src.Https != nil {
|
||||
if *src.Https.Http2Status == "on" {
|
||||
http2 := int32(1)
|
||||
dest.Http2 = &http2
|
||||
}
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
@@ -8,11 +8,9 @@ import (
|
||||
k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type KubernetesAccess struct {
|
||||
KubeConfig string `json:"kubeConfig"`
|
||||
}
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type K8sSecretDeployer struct {
|
||||
option *DeployerOption
|
||||
@@ -27,7 +25,7 @@ func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) GetInfo() []string {
|
||||
@@ -35,7 +33,7 @@ func (d *K8sSecretDeployer) GetInfo() []string {
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
|
||||
access := &KubernetesAccess{}
|
||||
access := &domain.KubernetesAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -86,7 +84,7 @@ func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) createClient(access *KubernetesAccess) (*kubernetes.Clientset, error) {
|
||||
func (d *K8sSecretDeployer) createClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) {
|
||||
kubeConfig, err := clientcmd.Load([]byte(access.KubeConfig))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -8,9 +8,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type LocalAccess struct{}
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type LocalDeployer struct {
|
||||
option *DeployerOption
|
||||
@@ -25,7 +25,7 @@ func NewLocalDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *LocalDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *LocalDeployer) GetInfo() []string {
|
||||
@@ -33,7 +33,7 @@ func (d *LocalDeployer) GetInfo() []string {
|
||||
}
|
||||
|
||||
func (d *LocalDeployer) Deploy(ctx context.Context) error {
|
||||
access := &LocalAccess{}
|
||||
access := &domain.LocalAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ func NewQiniuCDNDeployer(option *DeployerOption) (*QiniuCDNDeployer, error) {
|
||||
}
|
||||
|
||||
func (d *QiniuCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *QiniuCDNDeployer) GetInfo() []string {
|
||||
|
@@ -10,16 +10,9 @@ import (
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
sshPkg "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type SSHAccess struct {
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Key string `json:"key"`
|
||||
KeyPassphrase string `json:"keyPassphrase"`
|
||||
}
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type SSHDeployer struct {
|
||||
option *DeployerOption
|
||||
@@ -34,7 +27,7 @@ func NewSSHDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *SSHDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *SSHDeployer) GetInfo() []string {
|
||||
@@ -42,7 +35,7 @@ func (d *SSHDeployer) GetInfo() []string {
|
||||
}
|
||||
|
||||
func (d *SSHDeployer) Deploy(ctx context.Context) error {
|
||||
access := &SSHAccess{}
|
||||
access := &domain.SSHAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,7 +123,7 @@ func (d *SSHDeployer) upload(client *sshPkg.Client, content, path string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSHDeployer) createClient(access *SSHAccess) (*sshPkg.Client, error) {
|
||||
func (d *SSHDeployer) createClient(access *domain.SSHAccess) (*sshPkg.Client, error) {
|
||||
var authMethod sshPkg.AuthMethod
|
||||
|
||||
if access.Key != "" {
|
||||
|
@@ -41,7 +41,7 @@ func NewTencentCDNDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *TencentCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *TencentCDNDeployer) GetInfo() []string {
|
||||
|
@@ -7,13 +7,10 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"certimate/internal/domain"
|
||||
xhttp "certimate/internal/utils/http"
|
||||
)
|
||||
|
||||
type WebhookAccess struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type WebhookDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
@@ -27,7 +24,7 @@ func NewWebhookDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) GetInfo() []string {
|
||||
@@ -42,7 +39,7 @@ type webhookData struct {
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) Deploy(ctx context.Context) error {
|
||||
access := &WebhookAccess{}
|
||||
access := &domain.WebhookAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return fmt.Errorf("failed to parse hook access config: %w", err)
|
||||
}
|
||||
|
@@ -51,4 +51,21 @@ type HttpreqAccess struct {
|
||||
Mode string `json:"mode"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
type LocalAccess struct{}
|
||||
|
||||
type SSHAccess struct {
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Key string `json:"key"`
|
||||
KeyPassphrase string `json:"keyPassphrase"`
|
||||
}
|
||||
|
||||
type WebhookAccess struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type KubernetesAccess struct {
|
||||
KubeConfig string `json:"kubeConfig"`
|
||||
}
|
||||
|
23
internal/domain/err.go
Normal file
23
internal/domain/err.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package domain
|
||||
|
||||
var ErrAuthFailed = NewXError(4999, "auth failed")
|
||||
|
||||
type XError struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func NewXError(code int, msg string) *XError {
|
||||
return &XError{code, msg}
|
||||
}
|
||||
|
||||
func (e *XError) Error() string {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
func (e *XError) GetCode() int {
|
||||
if e.Code == 0 {
|
||||
return 100
|
||||
}
|
||||
return e.Code
|
||||
}
|
12
internal/domain/notify.go
Normal file
12
internal/domain/notify.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package domain
|
||||
|
||||
const (
|
||||
NotifyChannelDingtalk = "dingtalk"
|
||||
NotifyChannelWebhook = "webhook"
|
||||
NotifyChannelTelegram = "telegram"
|
||||
NotifyChannelLark = "lark"
|
||||
)
|
||||
|
||||
type NotifyTestPushReq struct {
|
||||
Channel string `json:"channel"`
|
||||
}
|
31
internal/domain/setting.go
Normal file
31
internal/domain/setting.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
}
|
||||
|
||||
type ChannelsConfig map[string]map[string]any
|
||||
|
||||
func (s *Setting) GetChannelContent(channel string) (map[string]any, error) {
|
||||
conf := &ChannelsConfig{}
|
||||
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, ok := (*conf)[channel]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("channel %s not found", channel)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
|
||||
notifyPackage "github.com/nikoksr/notify"
|
||||
@@ -14,13 +15,6 @@ import (
|
||||
"github.com/nikoksr/notify/service/telegram"
|
||||
)
|
||||
|
||||
const (
|
||||
notifyChannelDingtalk = "dingtalk"
|
||||
notifyChannelWebhook = "webhook"
|
||||
notifyChannelTelegram = "telegram"
|
||||
notifyChannelLark = "lark"
|
||||
)
|
||||
|
||||
func Send(title, content string) error {
|
||||
// 获取所有的推送渠道
|
||||
notifiers, err := getNotifiers()
|
||||
@@ -39,6 +33,28 @@ func Send(title, content string) error {
|
||||
return n.Send(context.Background(), title, content)
|
||||
}
|
||||
|
||||
type sendTestParam struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Channel string `json:"channel"`
|
||||
Conf map[string]any `json:"conf"`
|
||||
}
|
||||
|
||||
func SendTest(param *sendTestParam) error {
|
||||
notifier, err := getNotifier(param.Channel, param.Conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n := notifyPackage.New()
|
||||
|
||||
// 添加推送渠道
|
||||
n.UseServices(notifier)
|
||||
|
||||
// 发送消息
|
||||
return n.Send(context.Background(), param.Title, param.Content)
|
||||
}
|
||||
|
||||
func getNotifiers() ([]notifyPackage.Notifier, error) {
|
||||
resp, err := app.GetApp().Dao().FindFirstRecordByFilter("settings", "name='notifyChannels'")
|
||||
if err != nil {
|
||||
@@ -59,27 +75,38 @@ func getNotifiers() ([]notifyPackage.Notifier, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch k {
|
||||
case notifyChannelTelegram:
|
||||
temp := getTelegramNotifier(v)
|
||||
if temp == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
notifiers = append(notifiers, temp)
|
||||
case notifyChannelDingtalk:
|
||||
notifiers = append(notifiers, getDingTalkNotifier(v))
|
||||
case notifyChannelLark:
|
||||
notifiers = append(notifiers, getLarkNotifier(v))
|
||||
case notifyChannelWebhook:
|
||||
notifiers = append(notifiers, getWebhookNotifier(v))
|
||||
notifier, err := getNotifier(k, v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
notifiers = append(notifiers, notifier)
|
||||
|
||||
}
|
||||
|
||||
return notifiers, nil
|
||||
}
|
||||
|
||||
func getNotifier(channel string, conf map[string]any) (notifyPackage.Notifier, error) {
|
||||
switch channel {
|
||||
case domain.NotifyChannelTelegram:
|
||||
temp := getTelegramNotifier(conf)
|
||||
if temp == nil {
|
||||
return nil, fmt.Errorf("telegram notifier config error")
|
||||
}
|
||||
|
||||
return temp, nil
|
||||
case domain.NotifyChannelDingtalk:
|
||||
return getDingTalkNotifier(conf), nil
|
||||
case domain.NotifyChannelLark:
|
||||
return getLarkNotifier(conf), nil
|
||||
case domain.NotifyChannelWebhook:
|
||||
return getWebhookNotifier(conf), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("notifier not found")
|
||||
}
|
||||
|
||||
func getWebhookNotifier(conf map[string]any) notifyPackage.Notifier {
|
||||
rs := http.New()
|
||||
|
||||
|
46
internal/notify/service.go
Normal file
46
internal/notify/service.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
const (
|
||||
notifyTestTitle = "测试通知"
|
||||
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
|
||||
)
|
||||
|
||||
type SettingRepository interface {
|
||||
GetByName(ctx context.Context, name string) (*domain.Setting, error)
|
||||
}
|
||||
|
||||
type NotifyService struct {
|
||||
settingRepo SettingRepository
|
||||
}
|
||||
|
||||
func NewNotifyService(settingRepo SettingRepository) *NotifyService {
|
||||
return &NotifyService{
|
||||
settingRepo: settingRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifyService) Test(ctx context.Context, req *domain.NotifyTestPushReq) error {
|
||||
setting, err := n.settingRepo.GetByName(ctx, "notifyChannels")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get notify channels setting failed: %w", err)
|
||||
}
|
||||
|
||||
conf, err := setting.GetChannelContent(req.Channel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get notify channel %s config failed: %w", req.Channel, err)
|
||||
}
|
||||
|
||||
return SendTest(&sendTestParam{
|
||||
Title: notifyTestTitle,
|
||||
Content: notifyTestBody,
|
||||
Channel: req.Channel,
|
||||
Conf: conf,
|
||||
})
|
||||
}
|
31
internal/repository/setting.go
Normal file
31
internal/repository/setting.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
)
|
||||
|
||||
type SettingRepository struct{}
|
||||
|
||||
func NewSettingRepository() *SettingRepository {
|
||||
return &SettingRepository{}
|
||||
}
|
||||
|
||||
func (s *SettingRepository) GetByName(ctx context.Context, name string) (*domain.Setting, error) {
|
||||
resp, err := app.GetApp().Dao().FindFirstRecordByFilter("settings", "name='"+name+"'")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rs := &domain.Setting{
|
||||
ID: resp.GetString("id"),
|
||||
Name: resp.GetString("name"),
|
||||
Content: resp.GetString("content"),
|
||||
Created: resp.GetTime("created"),
|
||||
Updated: resp.GetTime("updated"),
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
}
|
41
internal/rest/notify.go
Normal file
41
internal/rest/notify.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/resp"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
type NotifyService interface {
|
||||
Test(ctx context.Context, req *domain.NotifyTestPushReq) error
|
||||
}
|
||||
|
||||
type notifyHandler struct {
|
||||
service NotifyService
|
||||
}
|
||||
|
||||
func NewNotifyHandler(route *echo.Group, service NotifyService) {
|
||||
handler := ¬ifyHandler{
|
||||
service: service,
|
||||
}
|
||||
|
||||
group := route.Group("/notify")
|
||||
|
||||
group.POST("/test", handler.test)
|
||||
}
|
||||
|
||||
func (handler *notifyHandler) test(c echo.Context) error {
|
||||
req := &domain.NotifyTestPushReq{}
|
||||
if err := c.Bind(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := handler.service.Test(c.Request().Context(), req); err != nil {
|
||||
return resp.Err(c, err)
|
||||
}
|
||||
|
||||
return resp.Succ(c, nil)
|
||||
}
|
19
internal/routes/routes.go
Normal file
19
internal/routes/routes.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"certimate/internal/notify"
|
||||
"certimate/internal/repository"
|
||||
"certimate/internal/rest"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
)
|
||||
|
||||
func Register(e *echo.Echo) {
|
||||
notifyRepo := repository.NewSettingRepository()
|
||||
notifySvc := notify.NewNotifyService(notifyRepo)
|
||||
|
||||
group := e.Group("/api", apis.RequireAdminAuth())
|
||||
|
||||
rest.NewNotifyHandler(group, notifySvc)
|
||||
}
|
39
internal/utils/resp/resp.go
Normal file
39
internal/utils/resp/resp.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"certimate/internal/domain"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func Succ(e echo.Context, data interface{}) error {
|
||||
rs := &Response{
|
||||
Code: 0,
|
||||
Msg: "success",
|
||||
Data: data,
|
||||
}
|
||||
return e.JSON(http.StatusOK, rs)
|
||||
}
|
||||
|
||||
func Err(e echo.Context, err error) error {
|
||||
xerr, ok := err.(*domain.XError)
|
||||
code := 100
|
||||
if ok {
|
||||
code = xerr.GetCode()
|
||||
}
|
||||
|
||||
rs := &Response{
|
||||
Code: code,
|
||||
Msg: err.Error(),
|
||||
Data: nil,
|
||||
}
|
||||
return e.JSON(http.StatusOK, rs)
|
||||
}
|
Reference in New Issue
Block a user