diff --git a/internal/acme/dns_provider.go b/internal/acme/dns_provider.go new file mode 100644 index 00000000..347c2ecb --- /dev/null +++ b/internal/acme/dns_provider.go @@ -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 +} diff --git a/internal/acme/key.go b/internal/acme/key.go new file mode 100644 index 00000000..722c8159 --- /dev/null +++ b/internal/acme/key.go @@ -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) +} diff --git a/internal/acme/request.go b/internal/acme/request.go new file mode 100644 index 00000000..3722e3b0 --- /dev/null +++ b/internal/acme/request.go @@ -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 +} diff --git a/internal/acme/request_test.go b/internal/acme/request_test.go new file mode 100644 index 00000000..1cba4a99 --- /dev/null +++ b/internal/acme/request_test.go @@ -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 +} diff --git a/internal/acme/task.go b/internal/acme/task.go new file mode 100644 index 00000000..81718d2a --- /dev/null +++ b/internal/acme/task.go @@ -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 +} diff --git a/internal/acme/user.go b/internal/acme/user.go new file mode 100644 index 00000000..38f429c0 --- /dev/null +++ b/internal/acme/user.go @@ -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) +} diff --git a/internal/db/models/acme_task_dao.go b/internal/db/models/acme_task_dao.go index 9d848799..113a70f9 100644 --- a/internal/db/models/acme_task_dao.go +++ b/internal/db/models/acme_task_dao.go @@ -2,10 +2,15 @@ package models import ( "encoding/json" + "github.com/TeaOSLab/EdgeAPI/internal/acme" + "github.com/TeaOSLab/EdgeAPI/internal/dnsclients" "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/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/types" ) @@ -131,7 +136,6 @@ func (this *ACMETaskDAO) CreateACMETask(adminId int64, userId int64, acmeUserId op.AutoRenew = autoRenew op.IsOn = true op.State = ACMETaskStateEnabled - op.IsOk = false _, err := this.Save(op) if err != nil { return 0, err @@ -173,3 +177,179 @@ func (this *ACMETaskDAO) CheckACMETask(adminId int64, userId int64, acmeTaskId i Pk(acmeTaskId). 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 +} diff --git a/internal/db/models/acme_task_log_dao.go b/internal/db/models/acme_task_log_dao.go index fd149e3c..aa80935d 100644 --- a/internal/db/models/acme_task_log_dao.go +++ b/internal/db/models/acme_task_log_dao.go @@ -26,3 +26,13 @@ func init() { 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 +} diff --git a/internal/db/models/acme_task_model.go b/internal/db/models/acme_task_model.go index 8eab2b8a..b9ec7aa0 100644 --- a/internal/db/models/acme_task_model.go +++ b/internal/db/models/acme_task_model.go @@ -12,7 +12,6 @@ type ACMETask struct { Domains string `field:"domains"` // 证书域名 CreatedAt uint64 `field:"createdAt"` // 创建时间 State uint8 `field:"state"` // 状态 - IsOk uint8 `field:"isOk"` // 最后运行是否正常 CertId uint64 `field:"certId"` // 生成的证书ID AutoRenew uint8 `field:"autoRenew"` // 是否自动更新 } @@ -28,7 +27,6 @@ type ACMETaskOperator struct { Domains interface{} // 证书域名 CreatedAt interface{} // 创建时间 State interface{} // 状态 - IsOk interface{} // 最后运行是否正常 CertId interface{} // 生成的证书ID AutoRenew interface{} // 是否自动更新 } diff --git a/internal/db/models/acme_user_dao.go b/internal/db/models/acme_user_dao.go index c74f1907..448e2ab7 100644 --- a/internal/db/models/acme_user_dao.go +++ b/internal/db/models/acme_user_dao.go @@ -97,7 +97,7 @@ func (this *ACMEUserDAO) CreateACMEUser(adminId int64, userId int64, email strin return types.Int64(op.Id), nil } -// 查找用户列表 +// 修改用户信息 func (this *ACMEUserDAO) UpdateACMEUser(acmeUserId int64, description string) error { if acmeUserId <= 0 { return errors.New("invalid acmeUserId") @@ -109,6 +109,18 @@ func (this *ACMEUserDAO) UpdateACMEUser(acmeUserId int64, description string) er 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) { query := this.Query() diff --git a/internal/db/models/acme_user_model.go b/internal/db/models/acme_user_model.go index 4f408e52..2f075bd2 100644 --- a/internal/db/models/acme_user_model.go +++ b/internal/db/models/acme_user_model.go @@ -2,25 +2,27 @@ package models // type ACMEUser struct { - Id uint64 `field:"id"` // ID - AdminId uint32 `field:"adminId"` // 管理员ID - UserId uint32 `field:"userId"` // 用户ID - PrivateKey string `field:"privateKey"` // 私钥 - Email string `field:"email"` // E-mail - CreatedAt uint64 `field:"createdAt"` // 创建时间 - State uint8 `field:"state"` // 状态 - Description string `field:"description"` // 备注介绍 + Id uint64 `field:"id"` // ID + AdminId uint32 `field:"adminId"` // 管理员ID + UserId uint32 `field:"userId"` // 用户ID + PrivateKey string `field:"privateKey"` // 私钥 + Email string `field:"email"` // E-mail + CreatedAt uint64 `field:"createdAt"` // 创建时间 + State uint8 `field:"state"` // 状态 + Description string `field:"description"` // 备注介绍 + Registration string `field:"registration"` // 注册信息 } type ACMEUserOperator struct { - Id interface{} // ID - AdminId interface{} // 管理员ID - UserId interface{} // 用户ID - PrivateKey interface{} // 私钥 - Email interface{} // E-mail - CreatedAt interface{} // 创建时间 - State interface{} // 状态 - Description interface{} // 备注介绍 + Id interface{} // ID + AdminId interface{} // 管理员ID + UserId interface{} // 用户ID + PrivateKey interface{} // 私钥 + Email interface{} // E-mail + CreatedAt interface{} // 创建时间 + State interface{} // 状态 + Description interface{} // 备注介绍 + Registration interface{} // 注册信息 } func NewACMEUserOperator() *ACMEUserOperator { diff --git a/internal/db/models/ssl_cert_dao.go b/internal/db/models/ssl_cert_dao.go index cb3a10ac..f19df485 100644 --- a/internal/db/models/ssl_cert_dao.go +++ b/internal/db/models/ssl_cert_dao.go @@ -178,6 +178,7 @@ func (this *SSLCertDAO) ComposeCertConfig(certId int64) (*sslconfigs.SSLCertConf config.Id = int64(cert.Id) config.IsOn = cert.IsOn == 1 config.IsCA = cert.IsCA == 1 + config.IsACME = cert.IsACME == 1 config.Name = cert.Name config.Description = cert.Description config.CertData = []byte(cert.CertData) @@ -269,3 +270,16 @@ func (this *SSLCertDAO) ListCertIds(isCA bool, isAvailable bool, isExpired bool, } 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 +} diff --git a/internal/dnsclients/provider_alidns.go b/internal/dnsclients/provider_alidns.go index be274734..ca21d459 100644 --- a/internal/dnsclients/provider_alidns.go +++ b/internal/dnsclients/provider_alidns.go @@ -11,6 +11,8 @@ import ( // 阿里云服务商 type AliDNSProvider struct { + BaseProvider + accessKeyId string accessKeySecret string } @@ -87,6 +89,20 @@ func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error 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 { req := alidns.CreateAddDomainRecordRequest() diff --git a/internal/dnsclients/provider_base.go b/internal/dnsclients/provider_base.go new file mode 100644 index 00000000..ae2cc40b --- /dev/null +++ b/internal/dnsclients/provider_base.go @@ -0,0 +1,4 @@ +package dnsclients + +type BaseProvider struct { +} diff --git a/internal/dnsclients/provider_dnspod.go b/internal/dnsclients/provider_dnspod.go index 35caceee..23e0e54f 100644 --- a/internal/dnsclients/provider_dnspod.go +++ b/internal/dnsclients/provider_dnspod.go @@ -14,6 +14,8 @@ import ( // DNSPod服务商 type DNSPodProvider struct { + BaseProvider + apiId string apiToken string } @@ -104,6 +106,20 @@ func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error 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 { if newRecord == nil { diff --git a/internal/dnsclients/provider_interface.go b/internal/dnsclients/provider_interface.go index e2ee8dee..6afe0622 100644 --- a/internal/dnsclients/provider_interface.go +++ b/internal/dnsclients/provider_interface.go @@ -7,12 +7,15 @@ type ProviderInterface interface { // 认证 Auth(params maps.Map) error - // 获取域名列表 + // 获取域名解析记录列表 GetRecords(domain string) (records []*Record, err error) // 读取域名支持的线路数据 GetRoutes(domain string) (routes []*Route, err error) + // 查询单个记录 + QueryRecord(domain string, name string, recordType RecordType) (*Record, error) + // 设置记录 AddRecord(domain string, newRecord *Record) error diff --git a/internal/errors/error.go b/internal/errors/error.go index 74faa7db..c873feca 100644 --- a/internal/errors/error.go +++ b/internal/errors/error.go @@ -2,6 +2,7 @@ package errors import ( "errors" + "github.com/iwind/TeaGo/Tea" "path/filepath" "runtime" "strconv" @@ -15,6 +16,11 @@ type errorObj struct { } func (this *errorObj) Error() string { + // 在非测试环境下,我们不提示详细的行数等信息 + if !Tea.IsTesting() { + return this.err.Error() + } + s := this.err.Error() + "\n " + this.file if len(this.funcName) > 0 { s += ":" + this.funcName + "()" diff --git a/internal/rpc/services/service_acme_task.go b/internal/rpc/services/service_acme_task.go index fe49e167..9c586be3 100644 --- a/internal/rpc/services/service_acme_task.go +++ b/internal/rpc/services/service_acme_task.go @@ -113,9 +113,11 @@ func (this *ACMETaskService) ListEnabledACMETasks(ctx context.Context, req *pb.L continue } pbCert = &pb.SSLCert{ - Id: int64(cert.Id), - IsOn: cert.IsOn == 1, - Name: cert.Name, + Id: int64(cert.Id), + IsOn: cert.IsOn == 1, + 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, Domains: task.DecodeDomains(), CreatedAt: int64(task.CreatedAt), - IsOk: task.IsOk == 1, AutoRenew: task.AutoRenew == 1, AcmeUser: pbACMEUser, DnsProvider: pbProvider, @@ -208,9 +209,13 @@ func (this *ACMETaskService) RunACMETask(ctx context.Context, req *pb.RunACMETas 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 } // 查找单个任务信息