mirror of
				https://github.com/TeaOSLab/EdgeAPI.git
				synced 2025-11-04 16:00:24 +08:00 
			
		
		
		
	[SSL证书]实现基本的自动申请证书流程
This commit is contained in:
		
							
								
								
									
										59
									
								
								internal/acme/dns_provider.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								internal/acme/dns_provider.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/challenge/dns01"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DNSProvider struct {
 | 
				
			||||||
 | 
						raw dnsclients.ProviderInterface
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDNSProvider(raw dnsclients.ProviderInterface) *DNSProvider {
 | 
				
			||||||
 | 
						return &DNSProvider{raw: raw}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *DNSProvider) Present(domain, token, keyAuth string) error {
 | 
				
			||||||
 | 
						fqdn, value := dns01.GetRecord(domain, keyAuth)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 设置记录
 | 
				
			||||||
 | 
						index := strings.Index(fqdn, "."+domain)
 | 
				
			||||||
 | 
						if index < 0 {
 | 
				
			||||||
 | 
							return errors.New("invalid fqdn value")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						recordName := fqdn[:index]
 | 
				
			||||||
 | 
						record, err := this.raw.QueryRecord(domain, recordName, dnsclients.RecordTypeTXT)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errors.New("query DNS record failed: " + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if record == nil {
 | 
				
			||||||
 | 
							err = this.raw.AddRecord(domain, &dnsclients.Record{
 | 
				
			||||||
 | 
								Id:    "",
 | 
				
			||||||
 | 
								Name:  recordName,
 | 
				
			||||||
 | 
								Type:  dnsclients.RecordTypeTXT,
 | 
				
			||||||
 | 
								Value: value,
 | 
				
			||||||
 | 
								Route: this.raw.DefaultRoute(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return errors.New("create DNS record failed: " + err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							err = this.raw.UpdateRecord(domain, record, &dnsclients.Record{
 | 
				
			||||||
 | 
								Name:  recordName,
 | 
				
			||||||
 | 
								Type:  dnsclients.RecordTypeTXT,
 | 
				
			||||||
 | 
								Value: value,
 | 
				
			||||||
 | 
								Route: this.raw.DefaultRoute(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return errors.New("update DNS record failed: " + err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *DNSProvider) CleanUp(domain, token, keyAuth string) error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								internal/acme/key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/acme/key.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/x509"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ParsePrivateKeyFromBase64(base64String string) (interface{}, error) {
 | 
				
			||||||
 | 
						data, err := base64.StdEncoding.DecodeString(base64String)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return x509.ParsePKCS8PrivateKey(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										94
									
								
								internal/acme/request.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								internal/acme/request.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/certcrypto"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/certificate"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/lego"
 | 
				
			||||||
 | 
						acmelog "github.com/go-acme/lego/v4/log"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/registration"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Request struct {
 | 
				
			||||||
 | 
						debug bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						task *Task
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRequest(task *Task) *Request {
 | 
				
			||||||
 | 
						return &Request{
 | 
				
			||||||
 | 
							task: task,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *Request) Debug() {
 | 
				
			||||||
 | 
						this.debug = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *Request) Run() (certData []byte, keyData []byte, err error) {
 | 
				
			||||||
 | 
						if !this.debug {
 | 
				
			||||||
 | 
							acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if this.task.User == nil {
 | 
				
			||||||
 | 
							err = errors.New("'user' must not be nil")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if this.task.DNSProvider == nil {
 | 
				
			||||||
 | 
							err = errors.New("'dnsProvider' must not be nil")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(this.task.DNSDomain) == 0 {
 | 
				
			||||||
 | 
							err = errors.New("'dnsDomain' must not be empty")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(this.task.Domains) == 0 {
 | 
				
			||||||
 | 
							err = errors.New("'domains' must not be empty")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := lego.NewConfig(this.task.User)
 | 
				
			||||||
 | 
						config.Certificate.KeyType = certcrypto.RSA2048
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := lego.NewClient(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 注册用户
 | 
				
			||||||
 | 
						resource := this.task.User.GetRegistration()
 | 
				
			||||||
 | 
						if resource != nil {
 | 
				
			||||||
 | 
							resource, err = client.Registration.QueryRegistration()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = this.task.User.Register(resource)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = client.Challenge.SetDNS01Provider(NewDNSProvider(this.task.DNSProvider))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 申请证书
 | 
				
			||||||
 | 
						request := certificate.ObtainRequest{
 | 
				
			||||||
 | 
							Domains: this.task.Domains,
 | 
				
			||||||
 | 
							Bundle:  true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						certResource, err := client.Certificate.Obtain(request)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return certResource.Certificate, certResource.PrivateKey, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								internal/acme/request_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								internal/acme/request_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/registration"
 | 
				
			||||||
 | 
						_ "github.com/go-sql-driver/mysql"
 | 
				
			||||||
 | 
						_ "github.com/iwind/TeaGo/bootstrap"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/dbs"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNewRequest(t *testing.T) {
 | 
				
			||||||
 | 
						privateKey, err := ParsePrivateKeyFromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgD3xxDXP4YVqHCfub21Yi3QL1Kvgow23J8CKJ7vU3L4+hRANCAARRl5ZKAlgGRc5RETSMYFCTXvjnePDgjALWgtgfClQGLB2rGyRecJvlesAM6Q7LQrDxVxvxdSQQmPGRqJGiBtjd")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user := NewUser("19644627@qq.com", privateKey, func(resource *registration.Resource) error {
 | 
				
			||||||
 | 
							resourceJSON, err := json.Marshal(resource)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t.Log(string(resourceJSON))
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						regResource := []byte(`{"body":{"status":"valid","contact":["mailto:19644627@qq.com"]},"uri":"https://acme-v02.api.letsencrypt.org/acme/acct/103672877"}`)
 | 
				
			||||||
 | 
						err = user.SetRegistration(regResource)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dnsProvider, err := testDNSPodProvider()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := NewRequest(&Task{
 | 
				
			||||||
 | 
							User:        user,
 | 
				
			||||||
 | 
							DNSProvider: dnsProvider,
 | 
				
			||||||
 | 
							DNSDomain:   "yun4s.cn",
 | 
				
			||||||
 | 
							Domains:     []string{"yun4s.cn"},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						certData, keyData, err := req.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Log("cert:", string(certData))
 | 
				
			||||||
 | 
						t.Log("key:", string(keyData))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testDNSPodProvider() (dnsclients.ProviderInterface, error) {
 | 
				
			||||||
 | 
						db, err := dbs.Default()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						one, err := db.FindOne("SELECT * FROM edgeDNSProviders WHERE type='dnspod' ORDER BY id DESC")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						apiParams := maps.Map{}
 | 
				
			||||||
 | 
						err = json.Unmarshal([]byte(one.GetString("apiParams")), &apiParams)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						provider := &dnsclients.DNSPodProvider{}
 | 
				
			||||||
 | 
						err = provider.Auth(apiParams)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return provider, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								internal/acme/task.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								internal/acme/task.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Task struct {
 | 
				
			||||||
 | 
						User        *User
 | 
				
			||||||
 | 
						DNSProvider dnsclients.ProviderInterface
 | 
				
			||||||
 | 
						DNSDomain   string
 | 
				
			||||||
 | 
						Domains     []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								internal/acme/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/acme/user.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					package acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/registration"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type User struct {
 | 
				
			||||||
 | 
						email        string
 | 
				
			||||||
 | 
						resource     *registration.Resource
 | 
				
			||||||
 | 
						key          crypto.PrivateKey
 | 
				
			||||||
 | 
						registerFunc func(resource *registration.Resource) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewUser(email string, key crypto.PrivateKey, registerFunc func(resource *registration.Resource) error) *User {
 | 
				
			||||||
 | 
						return &User{
 | 
				
			||||||
 | 
							email:        email,
 | 
				
			||||||
 | 
							key:          key,
 | 
				
			||||||
 | 
							registerFunc: registerFunc,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *User) GetEmail() string {
 | 
				
			||||||
 | 
						return this.email
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *User) GetRegistration() *registration.Resource {
 | 
				
			||||||
 | 
						return this.resource
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *User) SetRegistration(resourceData []byte) error {
 | 
				
			||||||
 | 
						resource := ®istration.Resource{}
 | 
				
			||||||
 | 
						err := json.Unmarshal(resourceData, resource)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.resource = resource
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *User) GetPrivateKey() crypto.PrivateKey {
 | 
				
			||||||
 | 
						return this.key
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *User) Register(resource *registration.Resource) error {
 | 
				
			||||||
 | 
						this.resource = resource
 | 
				
			||||||
 | 
						return this.registerFunc(resource)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,10 +2,15 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/acme"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
				
			||||||
 | 
						"github.com/go-acme/lego/v4/registration"
 | 
				
			||||||
	_ "github.com/go-sql-driver/mysql"
 | 
						_ "github.com/go-sql-driver/mysql"
 | 
				
			||||||
	"github.com/iwind/TeaGo/Tea"
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
	"github.com/iwind/TeaGo/dbs"
 | 
						"github.com/iwind/TeaGo/dbs"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/logs"
 | 
				
			||||||
	"github.com/iwind/TeaGo/types"
 | 
						"github.com/iwind/TeaGo/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,7 +136,6 @@ func (this *ACMETaskDAO) CreateACMETask(adminId int64, userId int64, acmeUserId
 | 
				
			|||||||
	op.AutoRenew = autoRenew
 | 
						op.AutoRenew = autoRenew
 | 
				
			||||||
	op.IsOn = true
 | 
						op.IsOn = true
 | 
				
			||||||
	op.State = ACMETaskStateEnabled
 | 
						op.State = ACMETaskStateEnabled
 | 
				
			||||||
	op.IsOk = false
 | 
					 | 
				
			||||||
	_, err := this.Save(op)
 | 
						_, err := this.Save(op)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -173,3 +177,179 @@ func (this *ACMETaskDAO) CheckACMETask(adminId int64, userId int64, acmeTaskId i
 | 
				
			|||||||
		Pk(acmeTaskId).
 | 
							Pk(acmeTaskId).
 | 
				
			||||||
		Exist()
 | 
							Exist()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 设置任务关联的证书
 | 
				
			||||||
 | 
					func (this *ACMETaskDAO) UpdateACMETaskCert(taskId int64, certId int64) error {
 | 
				
			||||||
 | 
						if taskId <= 0 {
 | 
				
			||||||
 | 
							return errors.New("invalid taskId")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						op := NewACMETaskOperator()
 | 
				
			||||||
 | 
						op.Id = taskId
 | 
				
			||||||
 | 
						op.CertId = certId
 | 
				
			||||||
 | 
						_, err := this.Save(op)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 执行任务并记录日志
 | 
				
			||||||
 | 
					func (this *ACMETaskDAO) RunTask(taskId int64) (isOk bool, errMsg string, resultCertId int64) {
 | 
				
			||||||
 | 
						isOk, errMsg, resultCertId = this.runTaskWithoutLog(taskId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 记录日志
 | 
				
			||||||
 | 
						err := SharedACMETaskLogDAO.CreateACMELog(taskId, isOk, errMsg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logs.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 执行任务但并不记录日志
 | 
				
			||||||
 | 
					func (this *ACMETaskDAO) runTaskWithoutLog(taskId int64) (isOk bool, errMsg string, resultCertId int64) {
 | 
				
			||||||
 | 
						task, err := this.FindEnabledACMETask(taskId)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "查询任务信息时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if task == nil {
 | 
				
			||||||
 | 
							errMsg = "找不到要执行的任务"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if task.IsOn != 1 {
 | 
				
			||||||
 | 
							errMsg = "任务没有启用"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ACME用户
 | 
				
			||||||
 | 
						user, err := SharedACMEUserDAO.FindEnabledACMEUser(int64(task.AcmeUserId))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "查询ACME用户时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if user == nil {
 | 
				
			||||||
 | 
							errMsg = "找不到ACME用户"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						privateKey, err := acme.ParsePrivateKeyFromBase64(user.PrivateKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "解析私钥时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remoteUser := acme.NewUser(user.Email, privateKey, func(resource *registration.Resource) error {
 | 
				
			||||||
 | 
							resourceJSON, err := json.Marshal(resource)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = SharedACMEUserDAO.UpdateACMEUserRegistration(int64(user.Id), resourceJSON)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(user.Registration) > 0 {
 | 
				
			||||||
 | 
							err = remoteUser.SetRegistration([]byte(user.Registration))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "设置注册信息时出错:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// DNS服务商
 | 
				
			||||||
 | 
						dnsProvider, err := SharedDNSProviderDAO.FindEnabledDNSProvider(int64(task.DnsProviderId))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "查找DNS服务商账号信息时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dnsProvider == nil {
 | 
				
			||||||
 | 
							errMsg = "找不到DNS服务商账号"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						providerInterface := dnsclients.FindProvider(dnsProvider.Type)
 | 
				
			||||||
 | 
						if providerInterface == nil {
 | 
				
			||||||
 | 
							errMsg = "暂不支持此类型的DNS服务商 '" + dnsProvider.Type + "'"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						apiParams, err := dnsProvider.DecodeAPIParams()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "解析DNS服务商API参数时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = providerInterface.Auth(apiParams)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "校验DNS服务商API参数时出错:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						acmeRequest := acme.NewRequest(&acme.Task{
 | 
				
			||||||
 | 
							User:        remoteUser,
 | 
				
			||||||
 | 
							DNSProvider: providerInterface,
 | 
				
			||||||
 | 
							DNSDomain:   task.DnsDomain,
 | 
				
			||||||
 | 
							Domains:     task.DecodeDomains(),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						certData, keyData, err := acmeRequest.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "证书生成失败:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 分析证书
 | 
				
			||||||
 | 
						sslConfig := &sslconfigs.SSLCertConfig{
 | 
				
			||||||
 | 
							CertData: certData,
 | 
				
			||||||
 | 
							KeyData:  keyData,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = sslConfig.Init()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg = "证书生成成功,但是分析证书信息时发生错误:" + err.Error()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 保存证书
 | 
				
			||||||
 | 
						resultCertId = int64(task.CertId)
 | 
				
			||||||
 | 
						if resultCertId > 0 {
 | 
				
			||||||
 | 
							cert, err := SharedSSLCertDAO.FindEnabledSSLCert(resultCertId)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "证书生成成功,但查询已绑定的证书时出错:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if cert == nil {
 | 
				
			||||||
 | 
								errMsg = "证书已被管理员或用户删除"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 禁用
 | 
				
			||||||
 | 
								err = SharedACMETaskDAO.DisableACMETask(taskId)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									errMsg = "禁用失效的ACME任务出错:" + err.Error()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = SharedSSLCertDAO.UpdateCert(resultCertId, cert.IsOn == 1, cert.Name, cert.Description, cert.ServerName, cert.IsCA == 1, certData, keyData, sslConfig.TimeBeginAt, sslConfig.TimeEndAt, sslConfig.DNSNames, sslConfig.CommonNames)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "证书生成成功,但是修改数据库中的证书信息时出错:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							resultCertId, err = SharedSSLCertDAO.CreateCert(int64(task.AdminId), int64(task.UserId), true, task.DnsDomain+"免费证书", "免费申请的证书", "", false, certData, keyData, sslConfig.TimeBeginAt, sslConfig.TimeEndAt, sslConfig.DNSNames, sslConfig.CommonNames)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "证书生成成功,但是保存到数据库失败:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = SharedSSLCertDAO.UpdateCertACME(resultCertId, int64(task.Id))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "证书生成成功,修改证书ACME信息时出错:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设置成功
 | 
				
			||||||
 | 
							err = SharedACMETaskDAO.UpdateACMETaskCert(taskId, resultCertId)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errMsg = "证书生成成功,设置任务关联的证书时出错:" + err.Error()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isOk = true
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,3 +26,13 @@ func init() {
 | 
				
			|||||||
		SharedACMETaskLogDAO = NewACMETaskLogDAO()
 | 
							SharedACMETaskLogDAO = NewACMETaskLogDAO()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 生成日志
 | 
				
			||||||
 | 
					func (this *ACMETaskLogDAO) CreateACMELog(taskId int64, isOk bool, errMsg string) error {
 | 
				
			||||||
 | 
						op := NewACMETaskLogOperator()
 | 
				
			||||||
 | 
						op.TaskId = taskId
 | 
				
			||||||
 | 
						op.Error = errMsg
 | 
				
			||||||
 | 
						op.IsOk = isOk
 | 
				
			||||||
 | 
						_, err := this.Save(op)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ type ACMETask struct {
 | 
				
			|||||||
	Domains       string `field:"domains"`       // 证书域名
 | 
						Domains       string `field:"domains"`       // 证书域名
 | 
				
			||||||
	CreatedAt     uint64 `field:"createdAt"`     // 创建时间
 | 
						CreatedAt     uint64 `field:"createdAt"`     // 创建时间
 | 
				
			||||||
	State         uint8  `field:"state"`         // 状态
 | 
						State         uint8  `field:"state"`         // 状态
 | 
				
			||||||
	IsOk          uint8  `field:"isOk"`          // 最后运行是否正常
 | 
					 | 
				
			||||||
	CertId        uint64 `field:"certId"`        // 生成的证书ID
 | 
						CertId        uint64 `field:"certId"`        // 生成的证书ID
 | 
				
			||||||
	AutoRenew     uint8  `field:"autoRenew"`     // 是否自动更新
 | 
						AutoRenew     uint8  `field:"autoRenew"`     // 是否自动更新
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -28,7 +27,6 @@ type ACMETaskOperator struct {
 | 
				
			|||||||
	Domains       interface{} // 证书域名
 | 
						Domains       interface{} // 证书域名
 | 
				
			||||||
	CreatedAt     interface{} // 创建时间
 | 
						CreatedAt     interface{} // 创建时间
 | 
				
			||||||
	State         interface{} // 状态
 | 
						State         interface{} // 状态
 | 
				
			||||||
	IsOk          interface{} // 最后运行是否正常
 | 
					 | 
				
			||||||
	CertId        interface{} // 生成的证书ID
 | 
						CertId        interface{} // 生成的证书ID
 | 
				
			||||||
	AutoRenew     interface{} // 是否自动更新
 | 
						AutoRenew     interface{} // 是否自动更新
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,7 +97,7 @@ func (this *ACMEUserDAO) CreateACMEUser(adminId int64, userId int64, email strin
 | 
				
			|||||||
	return types.Int64(op.Id), nil
 | 
						return types.Int64(op.Id), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 查找用户列表
 | 
					// 修改用户信息
 | 
				
			||||||
func (this *ACMEUserDAO) UpdateACMEUser(acmeUserId int64, description string) error {
 | 
					func (this *ACMEUserDAO) UpdateACMEUser(acmeUserId int64, description string) error {
 | 
				
			||||||
	if acmeUserId <= 0 {
 | 
						if acmeUserId <= 0 {
 | 
				
			||||||
		return errors.New("invalid acmeUserId")
 | 
							return errors.New("invalid acmeUserId")
 | 
				
			||||||
@@ -109,6 +109,18 @@ func (this *ACMEUserDAO) UpdateACMEUser(acmeUserId int64, description string) er
 | 
				
			|||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 修改用户ACME注册信息
 | 
				
			||||||
 | 
					func (this *ACMEUserDAO) UpdateACMEUserRegistration(acmeUserId int64, registrationJSON []byte) error {
 | 
				
			||||||
 | 
						if acmeUserId <= 0 {
 | 
				
			||||||
 | 
							return errors.New("invalid acmeUserId")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						op := NewACMEUserOperator()
 | 
				
			||||||
 | 
						op.Id = acmeUserId
 | 
				
			||||||
 | 
						op.Registration = registrationJSON
 | 
				
			||||||
 | 
						_, err := this.Save(op)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 计算用户数量
 | 
					// 计算用户数量
 | 
				
			||||||
func (this *ACMEUserDAO) CountACMEUsersWithAdminId(adminId int64, userId int64) (int64, error) {
 | 
					func (this *ACMEUserDAO) CountACMEUsersWithAdminId(adminId int64, userId int64) (int64, error) {
 | 
				
			||||||
	query := this.Query()
 | 
						query := this.Query()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ type ACMEUser struct {
 | 
				
			|||||||
	CreatedAt    uint64 `field:"createdAt"`    // 创建时间
 | 
						CreatedAt    uint64 `field:"createdAt"`    // 创建时间
 | 
				
			||||||
	State        uint8  `field:"state"`        // 状态
 | 
						State        uint8  `field:"state"`        // 状态
 | 
				
			||||||
	Description  string `field:"description"`  // 备注介绍
 | 
						Description  string `field:"description"`  // 备注介绍
 | 
				
			||||||
 | 
						Registration string `field:"registration"` // 注册信息
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ACMEUserOperator struct {
 | 
					type ACMEUserOperator struct {
 | 
				
			||||||
@@ -21,6 +22,7 @@ type ACMEUserOperator struct {
 | 
				
			|||||||
	CreatedAt    interface{} // 创建时间
 | 
						CreatedAt    interface{} // 创建时间
 | 
				
			||||||
	State        interface{} // 状态
 | 
						State        interface{} // 状态
 | 
				
			||||||
	Description  interface{} // 备注介绍
 | 
						Description  interface{} // 备注介绍
 | 
				
			||||||
 | 
						Registration interface{} // 注册信息
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewACMEUserOperator() *ACMEUserOperator {
 | 
					func NewACMEUserOperator() *ACMEUserOperator {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,6 +178,7 @@ func (this *SSLCertDAO) ComposeCertConfig(certId int64) (*sslconfigs.SSLCertConf
 | 
				
			|||||||
	config.Id = int64(cert.Id)
 | 
						config.Id = int64(cert.Id)
 | 
				
			||||||
	config.IsOn = cert.IsOn == 1
 | 
						config.IsOn = cert.IsOn == 1
 | 
				
			||||||
	config.IsCA = cert.IsCA == 1
 | 
						config.IsCA = cert.IsCA == 1
 | 
				
			||||||
 | 
						config.IsACME = cert.IsACME == 1
 | 
				
			||||||
	config.Name = cert.Name
 | 
						config.Name = cert.Name
 | 
				
			||||||
	config.Description = cert.Description
 | 
						config.Description = cert.Description
 | 
				
			||||||
	config.CertData = []byte(cert.CertData)
 | 
						config.CertData = []byte(cert.CertData)
 | 
				
			||||||
@@ -269,3 +270,16 @@ func (this *SSLCertDAO) ListCertIds(isCA bool, isAvailable bool, isExpired bool,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return result, nil
 | 
						return result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 设置证书的ACME信息
 | 
				
			||||||
 | 
					func (this *SSLCertDAO) UpdateCertACME(certId int64, acmeTaskId int64) error {
 | 
				
			||||||
 | 
						if certId <= 0 {
 | 
				
			||||||
 | 
							return errors.New("invalid certId")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						op := NewSSLCertOperator()
 | 
				
			||||||
 | 
						op.Id = certId
 | 
				
			||||||
 | 
						op.AcmeTaskId = acmeTaskId
 | 
				
			||||||
 | 
						op.IsACME = true
 | 
				
			||||||
 | 
						_, err := this.Save(op)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 阿里云服务商
 | 
					// 阿里云服务商
 | 
				
			||||||
type AliDNSProvider struct {
 | 
					type AliDNSProvider struct {
 | 
				
			||||||
 | 
						BaseProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	accessKeyId     string
 | 
						accessKeyId     string
 | 
				
			||||||
	accessKeySecret string
 | 
						accessKeySecret string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -87,6 +89,20 @@ func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 查询单个记录
 | 
				
			||||||
 | 
					func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
 | 
				
			||||||
 | 
						records, err := this.GetRecords(domain)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, record := range records {
 | 
				
			||||||
 | 
							if record.Name == name && record.Type == recordType {
 | 
				
			||||||
 | 
								return record, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 设置记录
 | 
					// 设置记录
 | 
				
			||||||
func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
 | 
					func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
 | 
				
			||||||
	req := alidns.CreateAddDomainRecordRequest()
 | 
						req := alidns.CreateAddDomainRecordRequest()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								internal/dnsclients/provider_base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								internal/dnsclients/provider_base.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					package dnsclients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BaseProvider struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,6 +14,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DNSPod服务商
 | 
					// DNSPod服务商
 | 
				
			||||||
type DNSPodProvider struct {
 | 
					type DNSPodProvider struct {
 | 
				
			||||||
 | 
						BaseProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiId    string
 | 
						apiId    string
 | 
				
			||||||
	apiToken string
 | 
						apiToken string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -104,6 +106,20 @@ func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error
 | 
				
			|||||||
	return routes, nil
 | 
						return routes, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 查询单个记录
 | 
				
			||||||
 | 
					func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
 | 
				
			||||||
 | 
						records, err := this.GetRecords(domain)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, record := range records {
 | 
				
			||||||
 | 
							if record.Name == name && record.Type == recordType {
 | 
				
			||||||
 | 
								return record, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 设置记录
 | 
					// 设置记录
 | 
				
			||||||
func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
 | 
					func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
 | 
				
			||||||
	if newRecord == nil {
 | 
						if newRecord == nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,12 +7,15 @@ type ProviderInterface interface {
 | 
				
			|||||||
	// 认证
 | 
						// 认证
 | 
				
			||||||
	Auth(params maps.Map) error
 | 
						Auth(params maps.Map) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 获取域名列表
 | 
						// 获取域名解析记录列表
 | 
				
			||||||
	GetRecords(domain string) (records []*Record, err error)
 | 
						GetRecords(domain string) (records []*Record, err error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 读取域名支持的线路数据
 | 
						// 读取域名支持的线路数据
 | 
				
			||||||
	GetRoutes(domain string) (routes []*Route, err error)
 | 
						GetRoutes(domain string) (routes []*Route, err error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 查询单个记录
 | 
				
			||||||
 | 
						QueryRecord(domain string, name string, recordType RecordType) (*Record, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 设置记录
 | 
						// 设置记录
 | 
				
			||||||
	AddRecord(domain string, newRecord *Record) error
 | 
						AddRecord(domain string, newRecord *Record) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package errors
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@@ -15,6 +16,11 @@ type errorObj struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *errorObj) Error() string {
 | 
					func (this *errorObj) Error() string {
 | 
				
			||||||
 | 
						// 在非测试环境下,我们不提示详细的行数等信息
 | 
				
			||||||
 | 
						if !Tea.IsTesting() {
 | 
				
			||||||
 | 
							return this.err.Error()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s := this.err.Error() + "\n  " + this.file
 | 
						s := this.err.Error() + "\n  " + this.file
 | 
				
			||||||
	if len(this.funcName) > 0 {
 | 
						if len(this.funcName) > 0 {
 | 
				
			||||||
		s += ":" + this.funcName + "()"
 | 
							s += ":" + this.funcName + "()"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,6 +116,8 @@ func (this *ACMETaskService) ListEnabledACMETasks(ctx context.Context, req *pb.L
 | 
				
			|||||||
				Id:          int64(cert.Id),
 | 
									Id:          int64(cert.Id),
 | 
				
			||||||
				IsOn:        cert.IsOn == 1,
 | 
									IsOn:        cert.IsOn == 1,
 | 
				
			||||||
				Name:        cert.Name,
 | 
									Name:        cert.Name,
 | 
				
			||||||
 | 
									TimeBeginAt: int64(cert.TimeBeginAt),
 | 
				
			||||||
 | 
									TimeEndAt:   int64(cert.TimeEndAt),
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -125,7 +127,6 @@ func (this *ACMETaskService) ListEnabledACMETasks(ctx context.Context, req *pb.L
 | 
				
			|||||||
			DnsDomain:   task.DnsDomain,
 | 
								DnsDomain:   task.DnsDomain,
 | 
				
			||||||
			Domains:     task.DecodeDomains(),
 | 
								Domains:     task.DecodeDomains(),
 | 
				
			||||||
			CreatedAt:   int64(task.CreatedAt),
 | 
								CreatedAt:   int64(task.CreatedAt),
 | 
				
			||||||
			IsOk:        task.IsOk == 1,
 | 
					 | 
				
			||||||
			AutoRenew:   task.AutoRenew == 1,
 | 
								AutoRenew:   task.AutoRenew == 1,
 | 
				
			||||||
			AcmeUser:    pbACMEUser,
 | 
								AcmeUser:    pbACMEUser,
 | 
				
			||||||
			DnsProvider: pbProvider,
 | 
								DnsProvider: pbProvider,
 | 
				
			||||||
@@ -208,9 +209,13 @@ func (this *ACMETaskService) RunACMETask(ctx context.Context, req *pb.RunACMETas
 | 
				
			|||||||
		return nil, this.PermissionError()
 | 
							return nil, this.PermissionError()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO
 | 
						isOk, msg, certId := models.SharedACMETaskDAO.RunTask(req.AcmeTaskId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &pb.RunACMETaskResponse{}, nil
 | 
						return &pb.RunACMETaskResponse{
 | 
				
			||||||
 | 
							IsOk:      isOk,
 | 
				
			||||||
 | 
							Error:     msg,
 | 
				
			||||||
 | 
							SslCertId: certId,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 查找单个任务信息
 | 
					// 查找单个任务信息
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user