diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go
index c4614c69..3a71cf36 100644
--- a/internal/rpc/rpc_client.go
+++ b/internal/rpc/rpc_client.go
@@ -220,6 +220,10 @@ func (this *RPCClient) ACMEUserRPC() pb.ACMEUserServiceClient {
return pb.NewACMEUserServiceClient(this.pickConn())
}
+func (this *RPCClient) ACMETaskRPC() pb.ACMETaskServiceClient {
+ return pb.NewACMETaskServiceClient(this.pickConn())
+}
+
// 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
diff --git a/internal/web/actions/default/dns/providers/delete.go b/internal/web/actions/default/dns/providers/delete.go
index 36c9202a..fda6ec12 100644
--- a/internal/web/actions/default/dns/providers/delete.go
+++ b/internal/web/actions/default/dns/providers/delete.go
@@ -19,14 +19,24 @@ func (this *DeleteAction) RunPost(params struct {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "删除DNS服务商 %d", params.ProviderId)
- // 检查是否被使用
+ // 检查是否被集群使用
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithDNSProviderId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithDNSProviderIdRequest{DnsProviderId: params.ProviderId})
if err != nil {
this.ErrorPage(err)
return
}
if countClustersResp.Count > 0 {
- this.Fail("当前DNS服务商账号正在被" + numberutils.FormatInt64(countClustersResp.Count) + "个集群所使用,所以不能删除。请修改后再操作。")
+ this.Fail("当前DNS服务商账号正在被" + numberutils.FormatInt64(countClustersResp.Count) + "个集群所使用,所以不能删除。请修改集群设置后再操作。")
+ }
+
+ // 判断是否被ACME任务使用
+ countACMETasksResp, err := this.RPC().ACMETaskRPC().CountEnabledACMETasksWithDNSProviderId(this.AdminContext(), &pb.CountEnabledACMETasksWithDNSProviderIdRequest{DnsProviderId: params.ProviderId})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ if countACMETasksResp.Count > 0 {
+ this.Fail("当前DNS服务商账号正在被" + numberutils.FormatInt64(countACMETasksResp.Count) + "个ACME证书申请任务使用,所以不能删除。请修改ACME证书申请任务后再操作。")
}
// 执行删除
diff --git a/internal/web/actions/default/dns/providers/index.go b/internal/web/actions/default/dns/providers/index.go
index b68dcd25..d8a3ec8a 100644
--- a/internal/web/actions/default/dns/providers/index.go
+++ b/internal/web/actions/default/dns/providers/index.go
@@ -16,7 +16,9 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct{}) {
- countResp, err := this.RPC().DNSProviderRPC().CountAllEnabledDNSProviders(this.AdminContext(), &pb.CountAllEnabledDNSProvidersRequest{})
+ countResp, err := this.RPC().DNSProviderRPC().CountAllEnabledDNSProviders(this.AdminContext(), &pb.CountAllEnabledDNSProvidersRequest{
+ AdminId: this.AdminId(),
+ })
if err != nil {
this.ErrorPage(err)
return
@@ -26,8 +28,9 @@ func (this *IndexAction) RunGet(params struct{}) {
this.Data["page"] = page.AsHTML()
providersResp, err := this.RPC().DNSProviderRPC().ListEnabledDNSProviders(this.AdminContext(), &pb.ListEnabledDNSProvidersRequest{
- Offset: page.Offset,
- Size: page.Size,
+ AdminId: this.AdminId(),
+ Offset: page.Offset,
+ Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
diff --git a/internal/web/actions/default/servers/certs/acme/create.go b/internal/web/actions/default/servers/certs/acme/create.go
index 091df02d..5a03d26a 100644
--- a/internal/web/actions/default/servers/certs/acme/create.go
+++ b/internal/web/actions/default/servers/certs/acme/create.go
@@ -1,15 +1,137 @@
package acme
-import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/actions"
+ "github.com/iwind/TeaGo/maps"
+ "strings"
+)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
- this.Nav("", "", "")
+ this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct{}) {
+ // 获取所有可用的用户
+ usersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.AdminContext(), &pb.FindAllACMEUsersRequest{
+ AdminId: this.AdminId(),
+ UserId: 0,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ userMaps := []maps.Map{}
+ for _, user := range usersResp.AcmeUsers {
+ description := user.Description
+ if len(description) > 0 {
+ description = "(" + description + ")"
+ }
+
+ userMaps = append(userMaps, maps.Map{
+ "id": user.Id,
+ "description": description,
+ "email": user.Email,
+ })
+ }
+ this.Data["users"] = userMaps
+
+ // 域名解析服务商
+ providersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
+ AdminId: this.AdminId(),
+ UserId: 0,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ providerMaps := []maps.Map{}
+ for _, provider := range providersResp.DnsProviders {
+ providerMaps = append(providerMaps, maps.Map{
+ "id": provider.Id,
+ "name": provider.Name,
+ "typeName": provider.TypeName,
+ })
+ }
+ this.Data["providers"] = providerMaps
+
this.Show()
}
+
+func (this *CreateAction) RunPost(params struct {
+ TaskId int64
+ AcmeUserId int64
+ DnsProviderId int64
+ DnsDomain string
+ Domains []string
+ AutoRenew bool
+
+ Must *actions.Must
+}) {
+ if params.AcmeUserId <= 0 {
+ this.Fail("请选择一个申请证书的用户")
+ }
+ if params.DnsProviderId <= 0 {
+ this.Fail("请选择DNS服务商")
+ }
+ if len(params.DnsDomain) == 0 {
+ this.Fail("请输入顶级域名")
+ }
+ dnsDomain := strings.ToLower(params.DnsDomain)
+ if !domainutils.ValidateDomainFormat(dnsDomain) {
+ this.Fail("请输入正确的顶级域名")
+ }
+
+ if len(params.Domains) == 0 {
+ this.Fail("请输入证书域名列表")
+ }
+ realDomains := []string{}
+ for _, domain := range params.Domains {
+ domain = strings.ToLower(domain)
+ if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
+ this.Fail("证书域名中的" + domain + "和顶级域名不一致")
+ }
+ realDomains = append(realDomains, domain)
+ }
+
+ if params.TaskId == 0 {
+ createResp, err := this.RPC().ACMETaskRPC().CreateACMETask(this.AdminContext(), &pb.CreateACMETaskRequest{
+ AcmeUserId: params.AcmeUserId,
+ DnsProviderId: params.DnsProviderId,
+ DnsDomain: dnsDomain,
+ Domains: realDomains,
+ AutoRenew: params.AutoRenew,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ params.TaskId = createResp.AcmeTaskId
+ defer this.CreateLogInfo("创建证书申请任务 %d", createResp.AcmeTaskId)
+ } else {
+ _, err := this.RPC().ACMETaskRPC().UpdateACMETask(this.AdminContext(), &pb.UpdateACMETaskRequest{
+ AcmeTaskId: params.TaskId,
+ AcmeUserId: params.AcmeUserId,
+ DnsProviderId: params.DnsProviderId,
+ DnsDomain: dnsDomain,
+ Domains: realDomains,
+ AutoRenew: params.AutoRenew,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ defer this.CreateLogInfo("修改证书申请任务 %d", params.TaskId)
+ }
+
+ this.Data["taskId"] = params.TaskId
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/servers/certs/acme/deleteTask.go b/internal/web/actions/default/servers/certs/acme/deleteTask.go
new file mode 100644
index 00000000..c4c1ce0b
--- /dev/null
+++ b/internal/web/actions/default/servers/certs/acme/deleteTask.go
@@ -0,0 +1,24 @@
+package acme
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+)
+
+type DeleteTaskAction struct {
+ actionutils.ParentAction
+}
+
+func (this *DeleteTaskAction) RunPost(params struct {
+ TaskId int64
+}) {
+ defer this.CreateLogInfo("删除证书申请任务 %d", params.TaskId)
+
+ _, err := this.RPC().ACMETaskRPC().DeleteACMETask(this.AdminContext(), &pb.DeleteACMETaskRequest{AcmeTaskId: params.TaskId})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/servers/certs/acme/index.go b/internal/web/actions/default/servers/certs/acme/index.go
index b7ee3e04..cf6f6c91 100644
--- a/internal/web/actions/default/servers/certs/acme/index.go
+++ b/internal/web/actions/default/servers/certs/acme/index.go
@@ -1,15 +1,75 @@
package acme
-import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/maps"
+)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
- this.Nav("", "", "cert")
+ this.Nav("", "", "task")
+ this.SecondMenu("list")
}
func (this *IndexAction) RunGet(params struct{}) {
+ countResp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
+ AdminId: this.AdminId(),
+ UserId: 0,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ count := countResp.Count
+ page := this.NewPage(count)
+ this.Data["page"] = page.AsHTML()
+
+ tasksResp, err := this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
+ AdminId: this.AdminId(),
+ UserId: 0,
+ Offset: page.Offset,
+ Size: page.Size,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ taskMaps := []maps.Map{}
+ for _, task := range tasksResp.AcmeTasks {
+ if task.AcmeUser == nil || task.DnsProvider == nil {
+ continue
+ }
+
+ var certMap maps.Map = nil
+ if task.SslCert != nil {
+ certMap = maps.Map{
+ "id": task.SslCert.Id,
+ "name": task.SslCert.Name,
+ }
+ }
+
+ taskMaps = append(taskMaps, maps.Map{
+ "id": task.Id,
+ "acmeUser": maps.Map{
+ "id": task.AcmeUser.Id,
+ "email": task.AcmeUser.Email,
+ },
+ "dnsProvider": maps.Map{
+ "id": task.DnsProvider.Id,
+ "name": task.DnsProvider.Name,
+ },
+ "dnsDomain": task.DnsDomain,
+ "domains": task.Domains,
+ "autoRenew": task.AutoRenew,
+ "cert": certMap,
+ })
+ }
+ this.Data["tasks"] = taskMaps
+
this.Show()
}
diff --git a/internal/web/actions/default/servers/certs/acme/run.go b/internal/web/actions/default/servers/certs/acme/run.go
new file mode 100644
index 00000000..986b90d2
--- /dev/null
+++ b/internal/web/actions/default/servers/certs/acme/run.go
@@ -0,0 +1,15 @@
+package acme
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "time"
+)
+
+type RunAction struct {
+ actionutils.ParentAction
+}
+
+func (this *RunAction) RunPost(params struct{}) {
+ time.Sleep(5 * time.Second) // TODO
+ this.Success()
+}
diff --git a/internal/web/actions/default/servers/certs/acme/updateTaskPopup.go b/internal/web/actions/default/servers/certs/acme/updateTaskPopup.go
new file mode 100644
index 00000000..efd0e9ef
--- /dev/null
+++ b/internal/web/actions/default/servers/certs/acme/updateTaskPopup.go
@@ -0,0 +1,142 @@
+package acme
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/actions"
+ "github.com/iwind/TeaGo/maps"
+ "strings"
+)
+
+type UpdateTaskPopupAction struct {
+ actionutils.ParentAction
+}
+
+func (this *UpdateTaskPopupAction) Init() {
+ this.Nav("", "", "")
+}
+
+func (this *UpdateTaskPopupAction) RunGet(params struct {
+ TaskId int64
+}) {
+ taskResp, err := this.RPC().ACMETaskRPC().FindEnabledACMETask(this.AdminContext(), &pb.FindEnabledACMETaskRequest{AcmeTaskId: params.TaskId})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ task := taskResp.AcmeTask
+ if task == nil {
+ this.NotFound("acmeTask", params.TaskId)
+ return
+ }
+
+ var dnsProviderMap maps.Map
+ if task.DnsProvider != nil {
+ dnsProviderMap = maps.Map{
+ "id": task.DnsProvider.Id,
+ }
+ } else {
+ dnsProviderMap = maps.Map{
+ "id": 0,
+ }
+ }
+
+ var acmeUserMap maps.Map
+ if task.AcmeUser != nil {
+ acmeUserMap = maps.Map{
+ "id": task.AcmeUser.Id,
+ }
+ } else {
+ acmeUserMap = maps.Map{
+ "id": 0,
+ }
+ }
+
+ this.Data["task"] = maps.Map{
+ "id": task.Id,
+ "acmeUser": acmeUserMap,
+ "dnsProviderId": task.DnsProvider.Id,
+ "dnsDomain": task.DnsDomain,
+ "domains": task.Domains,
+ "autoRenew": task.AutoRenew,
+ "isOn": task.IsOn,
+ "dnsProvider": dnsProviderMap,
+ }
+
+ // 域名解析服务商
+ providersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
+ AdminId: this.AdminId(),
+ UserId: 0,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ providerMaps := []maps.Map{}
+ for _, provider := range providersResp.DnsProviders {
+ providerMaps = append(providerMaps, maps.Map{
+ "id": provider.Id,
+ "name": provider.Name,
+ "typeName": provider.TypeName,
+ })
+ }
+ this.Data["providers"] = providerMaps
+
+ this.Show()
+}
+
+func (this *UpdateTaskPopupAction) RunPost(params struct {
+ TaskId int64
+ AcmeUserId int64
+ DnsProviderId int64
+ DnsDomain string
+ Domains []string
+ AutoRenew bool
+
+ Must *actions.Must
+ CSRF *actionutils.CSRF
+}) {
+ defer this.CreateLogInfo("修改证书申请任务 %d", params.TaskId)
+
+ if params.AcmeUserId <= 0 {
+ this.Fail("请选择一个申请证书的用户")
+ }
+ if params.DnsProviderId <= 0 {
+ this.Fail("请选择DNS服务商")
+ }
+ if len(params.DnsDomain) == 0 {
+ this.Fail("请输入顶级域名")
+ }
+ dnsDomain := strings.ToLower(params.DnsDomain)
+ if !domainutils.ValidateDomainFormat(dnsDomain) {
+ this.Fail("请输入正确的顶级域名")
+ }
+
+ if len(params.Domains) == 0 {
+ this.Fail("请输入证书域名列表")
+ }
+ realDomains := []string{}
+ for _, domain := range params.Domains {
+ domain = strings.ToLower(domain)
+ if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
+ this.Fail("证书域名中的" + domain + "和顶级域名不一致")
+ }
+ realDomains = append(realDomains, domain)
+ }
+
+ _, err := this.RPC().ACMETaskRPC().UpdateACMETask(this.AdminContext(), &pb.UpdateACMETaskRequest{
+ AcmeTaskId: params.TaskId,
+ AcmeUserId: params.AcmeUserId,
+ DnsProviderId: params.DnsProviderId,
+ DnsDomain: dnsDomain,
+ Domains: realDomains,
+ AutoRenew: params.AutoRenew,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/servers/certs/acme/users/createPopup.go b/internal/web/actions/default/servers/certs/acme/users/createPopup.go
index 829e04f4..41fd1828 100644
--- a/internal/web/actions/default/servers/certs/acme/users/createPopup.go
+++ b/internal/web/actions/default/servers/certs/acme/users/createPopup.go
@@ -4,6 +4,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
+ "github.com/iwind/TeaGo/maps"
)
type CreatePopupAction struct {
@@ -39,6 +40,13 @@ func (this *CreatePopupAction) RunPost(params struct {
return
}
+ // 返回数据
+ this.Data["acmeUser"] = maps.Map{
+ "id": createResp.AcmeUserId,
+ "description": params.Description,
+ "email": params.Email,
+ }
+
// 日志
defer this.CreateLogInfo("创建ACME用户 %d", createResp.AcmeUserId)
diff --git a/internal/web/actions/default/servers/certs/acme/users/delete.go b/internal/web/actions/default/servers/certs/acme/users/delete.go
index 0155e83c..048fef5e 100644
--- a/internal/web/actions/default/servers/certs/acme/users/delete.go
+++ b/internal/web/actions/default/servers/certs/acme/users/delete.go
@@ -14,13 +14,13 @@ func (this *DeleteAction) RunPost(params struct {
}) {
defer this.CreateLogInfo("删除ACME用户 %d", params.UserId)
- countResp, err := this.RPC().SSLCertRPC().CountSSLCertsWithACMEUserId(this.AdminContext(), &pb.CountSSLCertsWithACMEUserIdRequest{AcmeUserId: params.UserId})
+ countResp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasksWithACMEUserId(this.AdminContext(), &pb.CountAllEnabledACMETasksWithACMEUserIdRequest{AcmeUserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
- this.Fail("有证书正在和这个用户关联,所以不能删除")
+ this.Fail("有任务正在和这个用户关联,所以不能删除")
}
_, err = this.RPC().ACMEUserRPC().DeleteACMEUser(this.AdminContext(), &pb.DeleteACMEUserRequest{AcmeUserId: params.UserId})
diff --git a/internal/web/actions/default/servers/certs/helper.go b/internal/web/actions/default/servers/certs/helper.go
index 54697eed..ce54fa82 100644
--- a/internal/web/actions/default/servers/certs/helper.go
+++ b/internal/web/actions/default/servers/certs/helper.go
@@ -27,7 +27,7 @@ func (this *Helper) BeforeAction(action *actions.ActionObject) {
"isActive": action.Data.GetString("leftMenuItem") == "cert",
},
{
- "name": "免费证书",
+ "name": "申请证书",
"url": "/servers/certs/acme",
"isActive": action.Data.GetString("leftMenuItem") == "acme",
},
diff --git a/internal/web/actions/default/servers/certs/init.go b/internal/web/actions/default/servers/certs/init.go
index 7fb2d0c9..9af61b81 100644
--- a/internal/web/actions/default/servers/certs/init.go
+++ b/internal/web/actions/default/servers/certs/init.go
@@ -35,6 +35,9 @@ func init() {
Data("leftMenuItem", "acme").
Get("", new(acme.IndexAction)).
GetPost("/create", new(acme.CreateAction)).
+ Post("/run", new(acme.RunAction)).
+ GetPost("/updateTaskPopup", new(acme.UpdateTaskPopupAction)).
+ Post("/deleteTask", new(acme.DeleteTaskAction)).
Prefix("/servers/certs/acme/users").
Get("", new(users.IndexAction)).
diff --git a/internal/web/actions/default/servers/certs/viewCert.go b/internal/web/actions/default/servers/certs/viewCert.go
index 90923a67..d746097c 100644
--- a/internal/web/actions/default/servers/certs/viewCert.go
+++ b/internal/web/actions/default/servers/certs/viewCert.go
@@ -24,6 +24,11 @@ func (this *ViewCertAction) RunGet(params struct {
return
}
+ if len(certResp.CertJSON) == 0 {
+ this.NotFound("sslCert", params.CertId)
+ return
+ }
+
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.CertJSON, certConfig)
if err != nil {
diff --git a/web/views/@default/servers/certs/acme/@menu.html b/web/views/@default/servers/certs/acme/@menu.html
index 919facd6..9a6fbc42 100644
--- a/web/views/@default/servers/certs/acme/@menu.html
+++ b/web/views/@default/servers/certs/acme/@menu.html
@@ -1,4 +1,5 @@