mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +08:00 
			
		
		
		
	实现HTTPS配置
This commit is contained in:
		@@ -130,6 +130,10 @@ func (this *RPCClient) SSLCertRPC() pb.SSLCertServiceClient {
 | 
				
			|||||||
	return pb.NewSSLCertServiceClient(this.pickConn())
 | 
						return pb.NewSSLCertServiceClient(this.pickConn())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *RPCClient) SSLPolicyRPC() pb.SSLPolicyServiceClient {
 | 
				
			||||||
 | 
						return pb.NewSSLPolicyServiceClient(this.pickConn())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 构造上下文
 | 
					// 构造上下文
 | 
				
			||||||
func (this *RPCClient) Context(adminId int64) context.Context {
 | 
					func (this *RPCClient) Context(adminId int64) context.Context {
 | 
				
			||||||
	ctx := context.Background()
 | 
						ctx := context.Background()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					package ssl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 所有相关数据
 | 
				
			||||||
 | 
					type DatajsAction struct {
 | 
				
			||||||
 | 
						actionutils.ParentAction
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *DatajsAction) Init() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *DatajsAction) RunGet(params struct{}) {
 | 
				
			||||||
 | 
						this.AddHeader("Content-Type", "text/javascript; charset=utf-8")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							cipherSuitesJSON, err := json.Marshal(sslconfigs.AllTLSCipherSuites)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.WriteString("window.SSL_ALL_CIPHER_SUITES = " + string(cipherSuitesJSON) + ";\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							modernCipherSuitesJSON, err := json.Marshal(sslconfigs.TLSModernCipherSuites)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.WriteString("window.SSL_MODERN_CIPHER_SUITES = " + string(modernCipherSuitesJSON) + ";\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							intermediateCipherSuitesJSON, err := json.Marshal(sslconfigs.TLSIntermediateCipherSuites)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.WriteString("window.SSL_INTERMEDIATE_CIPHER_SUITES = " + string(intermediateCipherSuitesJSON) + ";\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							sslVersionsJSON, err := json.Marshal(sslconfigs.AllTlsVersions)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.WriteString("window.SSL_ALL_VERSIONS = " + string(sslVersionsJSON) + ";\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							clientAuthTypesJSON, err := json.Marshal(sslconfigs.AllSSLClientAuthTypes())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.WriteString("window.SSL_ALL_CLIENT_AUTH_TYPES = " + string(clientAuthTypesJSON) + ";\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -23,6 +23,8 @@ func init() {
 | 
				
			|||||||
			Get("/downloadKey", new(DownloadKeyAction)).
 | 
								Get("/downloadKey", new(DownloadKeyAction)).
 | 
				
			||||||
			Get("/downloadCert", new(DownloadCertAction)).
 | 
								Get("/downloadCert", new(DownloadCertAction)).
 | 
				
			||||||
			Get("/downloadZip", new(DownloadZipAction)).
 | 
								Get("/downloadZip", new(DownloadZipAction)).
 | 
				
			||||||
 | 
								Get("/selectPopup", new(SelectPopupAction)).
 | 
				
			||||||
 | 
								Get("/datajs", new(DatajsAction)).
 | 
				
			||||||
			EndAll()
 | 
								EndAll()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					package ssl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
 | 
						timeutil "github.com/iwind/TeaGo/utils/time"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 选择证书
 | 
				
			||||||
 | 
					type SelectPopupAction struct {
 | 
				
			||||||
 | 
						actionutils.ParentAction
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *SelectPopupAction) Init() {
 | 
				
			||||||
 | 
						this.Nav("", "", "")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *SelectPopupAction) RunGet(params struct{}) {
 | 
				
			||||||
 | 
						// TODO 支持关键词搜索
 | 
				
			||||||
 | 
						// TODO 列出常用的证书供用户选择
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.ErrorPage(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						page := this.NewPage(countResp.Count)
 | 
				
			||||||
 | 
						this.Data["page"] = page.AsHTML()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						listResp, err := this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
 | 
				
			||||||
 | 
							Offset: page.Offset,
 | 
				
			||||||
 | 
							Size:   page.Size,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						certConfigs := []*sslconfigs.SSLCertConfig{}
 | 
				
			||||||
 | 
						err = json.Unmarshal(listResp.CertsJSON, &certConfigs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.ErrorPage(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.Data["certs"] = certConfigs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						certMaps := []maps.Map{}
 | 
				
			||||||
 | 
						nowTime := time.Now().Unix()
 | 
				
			||||||
 | 
						for _, certConfig := range certConfigs {
 | 
				
			||||||
 | 
							countServersResp, err := this.RPC().ServerRPC().CountServersWithSSLCertId(this.AdminContext(), &pb.CountServersWithSSLCertIdRequest{CertId: certConfig.Id})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							certMaps = append(certMaps, maps.Map{
 | 
				
			||||||
 | 
								"beginDay":     timeutil.FormatTime("Y-m-d", certConfig.TimeBeginAt),
 | 
				
			||||||
 | 
								"endDay":       timeutil.FormatTime("Y-m-d", certConfig.TimeEndAt),
 | 
				
			||||||
 | 
								"isExpired":    nowTime > certConfig.TimeEndAt,
 | 
				
			||||||
 | 
								"isAvailable":  nowTime <= certConfig.TimeEndAt,
 | 
				
			||||||
 | 
								"countServers": countServersResp.Count,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.Data["certInfos"] = certMaps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.Show()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package ssl
 | 
					package ssl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
				
			||||||
@@ -73,7 +74,7 @@ func (this *UploadPopupAction) RunPost(params struct {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 保存
 | 
						// 保存
 | 
				
			||||||
	_, err = this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{
 | 
						createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{
 | 
				
			||||||
		IsOn:        params.IsOn,
 | 
							IsOn:        params.IsOn,
 | 
				
			||||||
		Name:        params.Name,
 | 
							Name:        params.Name,
 | 
				
			||||||
		Description: params.Description,
 | 
							Description: params.Description,
 | 
				
			||||||
@@ -91,5 +92,26 @@ func (this *UploadPopupAction) RunPost(params struct {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 查询已创建的证书并返回,方便调用者进行后续处理
 | 
				
			||||||
 | 
						certId := createResp.CertId
 | 
				
			||||||
 | 
						configResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{CertId: certId})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.ErrorPage(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						certConfig := &sslconfigs.SSLCertConfig{}
 | 
				
			||||||
 | 
						err = json.Unmarshal(configResp.CertJSON, certConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.ErrorPage(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						certConfig.CertData = nil // 去掉不必要的数据
 | 
				
			||||||
 | 
						certConfig.KeyData = nil  // 去掉不必要的数据
 | 
				
			||||||
 | 
						this.Data["cert"] = certConfig
 | 
				
			||||||
 | 
						this.Data["certRef"] = &sslconfigs.SSLCertRef{
 | 
				
			||||||
 | 
							IsOn:   true,
 | 
				
			||||||
 | 
							CertId: certId,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Success()
 | 
						this.Success()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,15 @@ package https
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
 | 
				
			||||||
	"github.com/iwind/TeaGo/actions"
 | 
						"github.com/iwind/TeaGo/actions"
 | 
				
			||||||
	"github.com/iwind/TeaGo/maps"
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IndexAction struct {
 | 
					type IndexAction struct {
 | 
				
			||||||
@@ -37,10 +40,29 @@ func (this *IndexAction) RunGet(params struct {
 | 
				
			|||||||
		httpsConfig.IsOn = true
 | 
							httpsConfig.IsOn = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var sslPolicy *sslconfigs.SSLPolicy
 | 
				
			||||||
 | 
						if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
 | 
				
			||||||
 | 
							sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON
 | 
				
			||||||
 | 
							if len(sslPolicyConfigJSON) > 0 {
 | 
				
			||||||
 | 
								sslPolicy = &sslconfigs.SSLPolicy{}
 | 
				
			||||||
 | 
								err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									this.ErrorPage(err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Data["serverType"] = server.Type
 | 
						this.Data["serverType"] = server.Type
 | 
				
			||||||
	this.Data["httpsConfig"] = maps.Map{
 | 
						this.Data["httpsConfig"] = maps.Map{
 | 
				
			||||||
		"isOn":      httpsConfig.IsOn,
 | 
							"isOn":      httpsConfig.IsOn,
 | 
				
			||||||
		"addresses": httpsConfig.Listen,
 | 
							"addresses": httpsConfig.Listen,
 | 
				
			||||||
 | 
							"sslPolicy": sslPolicy,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Show()
 | 
						this.Show()
 | 
				
			||||||
@@ -51,6 +73,8 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	IsOn      bool
 | 
						IsOn      bool
 | 
				
			||||||
	Addresses string
 | 
						Addresses string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SslPolicyJSON []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Must *actions.Must
 | 
						Must *actions.Must
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
	addresses := []*serverconfigs.NetworkAddressConfig{}
 | 
						addresses := []*serverconfigs.NetworkAddressConfig{}
 | 
				
			||||||
@@ -59,6 +83,73 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
		this.Fail("端口地址解析失败:" + err.Error())
 | 
							this.Fail("端口地址解析失败:" + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO 校验addresses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 校验SSL
 | 
				
			||||||
 | 
						var sslPolicyId = int64(0)
 | 
				
			||||||
 | 
						if params.SslPolicyJSON != nil {
 | 
				
			||||||
 | 
							sslPolicy := &sslconfigs.SSLPolicy{}
 | 
				
			||||||
 | 
							err = json.Unmarshal(params.SslPolicyJSON, sslPolicy)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(errors.New("解析SSL配置时发生了错误:" + err.Error()))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sslPolicyId = sslPolicy.Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							certsJSON, err := json.Marshal(sslPolicy.CertRefs)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							hstsJSON, err := json.Marshal(sslPolicy.HSTS)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clientCACertsJSON, err := json.Marshal(sslPolicy.ClientCARefs)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								this.ErrorPage(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if sslPolicyId > 0 {
 | 
				
			||||||
 | 
								_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
 | 
				
			||||||
 | 
									SslPolicyId:       sslPolicyId,
 | 
				
			||||||
 | 
									Http2Enabled:      sslPolicy.HTTP2Enabled,
 | 
				
			||||||
 | 
									MinVersion:        sslPolicy.MinVersion,
 | 
				
			||||||
 | 
									CertsJSON:         certsJSON,
 | 
				
			||||||
 | 
									HstsJSON:          hstsJSON,
 | 
				
			||||||
 | 
									ClientAuthType:    types.Int32(sslPolicy.ClientAuthType),
 | 
				
			||||||
 | 
									ClientCACertsJSON: clientCACertsJSON,
 | 
				
			||||||
 | 
									CipherSuitesIsOn:  sslPolicy.CipherSuitesIsOn,
 | 
				
			||||||
 | 
									CipherSuites:      sslPolicy.CipherSuites,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									this.ErrorPage(err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
 | 
				
			||||||
 | 
									Http2Enabled:      sslPolicy.HTTP2Enabled,
 | 
				
			||||||
 | 
									MinVersion:        sslPolicy.MinVersion,
 | 
				
			||||||
 | 
									CertsJSON:         certsJSON,
 | 
				
			||||||
 | 
									HstsJSON:          hstsJSON,
 | 
				
			||||||
 | 
									ClientAuthType:    types.Int32(sslPolicy.ClientAuthType),
 | 
				
			||||||
 | 
									ClientCACertsJSON: clientCACertsJSON,
 | 
				
			||||||
 | 
									CipherSuitesIsOn:  sslPolicy.CipherSuitesIsOn,
 | 
				
			||||||
 | 
									CipherSuites:      sslPolicy.CipherSuites,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									this.ErrorPage(err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								sslPolicyId = resp.SslPolicyId
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server, _, isOk := serverutils.FindServer(this.Parent(), params.ServerId)
 | 
						server, _, isOk := serverutils.FindServer(this.Parent(), params.ServerId)
 | 
				
			||||||
	if !isOk {
 | 
						if !isOk {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -72,6 +163,10 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
 | 
				
			||||||
 | 
							IsOn:        true,
 | 
				
			||||||
 | 
							SSLPolicyId: sslPolicyId,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	httpsConfig.IsOn = params.IsOn
 | 
						httpsConfig.IsOn = params.IsOn
 | 
				
			||||||
	httpsConfig.Listen = addresses
 | 
						httpsConfig.Listen = addresses
 | 
				
			||||||
	configData, err := json.Marshal(httpsConfig)
 | 
						configData, err := json.Marshal(httpsConfig)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										483
									
								
								web/public/js/components/server/ssl-config-box.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								web/public/js/components/server/ssl-config-box.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,483 @@
 | 
				
			|||||||
 | 
					Vue.component("ssl-config-box", {
 | 
				
			||||||
 | 
						props: ["v-ssl-policy", "v-protocol"],
 | 
				
			||||||
 | 
						created: function () {
 | 
				
			||||||
 | 
							let that = this
 | 
				
			||||||
 | 
							setTimeout(function () {
 | 
				
			||||||
 | 
								that.sortableCipherSuites()
 | 
				
			||||||
 | 
							}, 100)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						data: function () {
 | 
				
			||||||
 | 
							let policy = this.vSslPolicy
 | 
				
			||||||
 | 
							if (policy == null) {
 | 
				
			||||||
 | 
								policy = {
 | 
				
			||||||
 | 
									id: 0,
 | 
				
			||||||
 | 
									isOn: true,
 | 
				
			||||||
 | 
									certRefs: [],
 | 
				
			||||||
 | 
									certs: [],
 | 
				
			||||||
 | 
									clientCARefs: [],
 | 
				
			||||||
 | 
									clientCACerts: [],
 | 
				
			||||||
 | 
									clientAuthType: 0,
 | 
				
			||||||
 | 
									minVersion: "TLS 1.1",
 | 
				
			||||||
 | 
									hsts: null,
 | 
				
			||||||
 | 
									cipherSuitesIsOn: false,
 | 
				
			||||||
 | 
									cipherSuites: [],
 | 
				
			||||||
 | 
									http2Enabled: true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (policy.certRefs == null) {
 | 
				
			||||||
 | 
									policy.certRefs = []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (policy.certs == null) {
 | 
				
			||||||
 | 
									policy.certs = []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (policy.clientCARefs == null) {
 | 
				
			||||||
 | 
									policy.clientCARefs = []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (policy.clientCACerts == null) {
 | 
				
			||||||
 | 
									policy.clientCACerts = []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (policy.cipherSuites == null) {
 | 
				
			||||||
 | 
									policy.cipherSuites = []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let hsts = policy.hsts
 | 
				
			||||||
 | 
							if (hsts == null) {
 | 
				
			||||||
 | 
								hsts = {
 | 
				
			||||||
 | 
									isOn: false,
 | 
				
			||||||
 | 
									maxAge: 0,
 | 
				
			||||||
 | 
									includeSubDomains: false,
 | 
				
			||||||
 | 
									preload: false,
 | 
				
			||||||
 | 
									domains: []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								policy: policy,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// hsts
 | 
				
			||||||
 | 
								hsts: hsts,
 | 
				
			||||||
 | 
								hstsOptionsVisible: false,
 | 
				
			||||||
 | 
								hstsDomainAdding: false,
 | 
				
			||||||
 | 
								addingHstsDomain: "",
 | 
				
			||||||
 | 
								hstsDomainEditingIndex: -1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 相关数据
 | 
				
			||||||
 | 
								allVersions: window.SSL_ALL_VERSIONS,
 | 
				
			||||||
 | 
								allCipherSuites: window.SSL_ALL_CIPHER_SUITES.$copy(),
 | 
				
			||||||
 | 
								modernCipherSuites: window.SSL_MODERN_CIPHER_SUITES,
 | 
				
			||||||
 | 
								intermediateCipherSuites: window.SSL_INTERMEDIATE_CIPHER_SUITES,
 | 
				
			||||||
 | 
								allClientAuthTypes: window.SSL_ALL_CLIENT_AUTH_TYPES,
 | 
				
			||||||
 | 
								cipherSuitesVisible: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 高级选项
 | 
				
			||||||
 | 
								moreOptionsVisible: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						watch: {
 | 
				
			||||||
 | 
							hsts: {
 | 
				
			||||||
 | 
								deep: true,
 | 
				
			||||||
 | 
								handler: function () {
 | 
				
			||||||
 | 
									this.policy.hsts = this.hsts
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							// 删除证书
 | 
				
			||||||
 | 
							removeCert: function (index) {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前服务不再使用此证书。", function () {
 | 
				
			||||||
 | 
									that.policy.certRefs.$remove(index)
 | 
				
			||||||
 | 
									that.policy.certs.$remove(index)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 选择证书
 | 
				
			||||||
 | 
							selectCert: function () {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.popup("/servers/components/ssl/selectPopup", {
 | 
				
			||||||
 | 
									width: "50em",
 | 
				
			||||||
 | 
									height: "30em",
 | 
				
			||||||
 | 
									callback: function (resp) {
 | 
				
			||||||
 | 
										that.policy.certRefs.push(resp.data.certRef)
 | 
				
			||||||
 | 
										that.policy.certs.push(resp.data.cert)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 上传证书
 | 
				
			||||||
 | 
							uploadCert: function () {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.popup("/servers/components/ssl/uploadPopup", {
 | 
				
			||||||
 | 
									height: "28em",
 | 
				
			||||||
 | 
									callback: function (resp) {
 | 
				
			||||||
 | 
										teaweb.success("上传成功", function () {
 | 
				
			||||||
 | 
											that.policy.certRefs.push(resp.data.certRef)
 | 
				
			||||||
 | 
											that.policy.certs.push(resp.data.cert)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 更多选项
 | 
				
			||||||
 | 
							changeOptionsVisible: function () {
 | 
				
			||||||
 | 
								this.moreOptionsVisible = !this.moreOptionsVisible
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 格式化时间
 | 
				
			||||||
 | 
							formatTime: function (timestamp) {
 | 
				
			||||||
 | 
								return new Date(timestamp * 1000).format("Y-m-d")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 格式化加密套件
 | 
				
			||||||
 | 
							formatCipherSuite: function (cipherSuite) {
 | 
				
			||||||
 | 
								return cipherSuite.replace(/(AES|3DES)/, "<var style=\"font-weight: bold\">$1</var>")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 添加单个套件
 | 
				
			||||||
 | 
							addCipherSuite: function (cipherSuite) {
 | 
				
			||||||
 | 
								if (!this.policy.cipherSuites.$contains(cipherSuite)) {
 | 
				
			||||||
 | 
									this.policy.cipherSuites.push(cipherSuite)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								this.allCipherSuites.$removeValue(cipherSuite)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 删除单个套件
 | 
				
			||||||
 | 
							removeCipherSuite: function (cipherSuite) {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.confirm("确定要删除此套件吗?", function () {
 | 
				
			||||||
 | 
									that.policy.cipherSuites.$removeValue(cipherSuite)
 | 
				
			||||||
 | 
									that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$findAll(function (k, v) {
 | 
				
			||||||
 | 
										return !that.policy.cipherSuites.$contains(v)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 清除所选套件
 | 
				
			||||||
 | 
							clearCipherSuites: function () {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.confirm("确定要清除所有已选套件吗?", function () {
 | 
				
			||||||
 | 
									that.policy.cipherSuites = []
 | 
				
			||||||
 | 
									that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$copy()
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 批量添加套件
 | 
				
			||||||
 | 
							addBatchCipherSuites: function (suites) {
 | 
				
			||||||
 | 
								var that = this
 | 
				
			||||||
 | 
								teaweb.confirm("确定要批量添加套件?", function () {
 | 
				
			||||||
 | 
									suites.$each(function (k, v) {
 | 
				
			||||||
 | 
										if (that.policy.cipherSuites.$contains(v)) {
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										that.policy.cipherSuites.push(v)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * 套件拖动排序
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							sortableCipherSuites: function () {
 | 
				
			||||||
 | 
								var box = document.querySelector(".cipher-suites-box")
 | 
				
			||||||
 | 
								Sortable.create(box, {
 | 
				
			||||||
 | 
									draggable: ".label",
 | 
				
			||||||
 | 
									handle: ".icon.handle",
 | 
				
			||||||
 | 
									onStart: function () {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									onUpdate: function (event) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 显示所有套件
 | 
				
			||||||
 | 
							showAllCipherSuites: function () {
 | 
				
			||||||
 | 
								this.cipherSuitesVisible = !this.cipherSuitesVisible
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 显示HSTS更多选项
 | 
				
			||||||
 | 
							showMoreHSTS: function () {
 | 
				
			||||||
 | 
								this.hstsOptionsVisible = !this.hstsOptionsVisible;
 | 
				
			||||||
 | 
								if (this.hstsOptionsVisible) {
 | 
				
			||||||
 | 
									this.changeHSTSMaxAge()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 监控HSTS有效期修改
 | 
				
			||||||
 | 
							changeHSTSMaxAge: function () {
 | 
				
			||||||
 | 
								var v = this.hsts.maxAge
 | 
				
			||||||
 | 
								if (isNaN(v)) {
 | 
				
			||||||
 | 
									this.hsts.days = "-"
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								this.hsts.days = parseInt(v / 86400)
 | 
				
			||||||
 | 
								if (isNaN(this.hsts.days)) {
 | 
				
			||||||
 | 
									this.hsts.days = "-"
 | 
				
			||||||
 | 
								} else if (this.hsts.days < 0) {
 | 
				
			||||||
 | 
									this.hsts.days = "-"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设置HSTS有效期
 | 
				
			||||||
 | 
							setHSTSMaxAge: function (maxAge) {
 | 
				
			||||||
 | 
								this.hsts.maxAge = maxAge
 | 
				
			||||||
 | 
								this.changeHSTSMaxAge()
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 添加HSTS域名
 | 
				
			||||||
 | 
							addHstsDomain: function () {
 | 
				
			||||||
 | 
								this.hstsDomainAdding = true
 | 
				
			||||||
 | 
								this.hstsDomainEditingIndex = -1
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								setTimeout(function () {
 | 
				
			||||||
 | 
									that.$refs.addingHstsDomain.focus()
 | 
				
			||||||
 | 
								}, 100)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 修改HSTS域名
 | 
				
			||||||
 | 
							editHstsDomain: function (index) {
 | 
				
			||||||
 | 
								this.hstsDomainEditingIndex = index
 | 
				
			||||||
 | 
								this.addingHstsDomain = this.hsts.domains[index]
 | 
				
			||||||
 | 
								this.hstsDomainAdding = true
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								setTimeout(function () {
 | 
				
			||||||
 | 
									that.$refs.addingHstsDomain.focus()
 | 
				
			||||||
 | 
								}, 100)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 确认HSTS域名添加
 | 
				
			||||||
 | 
							confirmAddHstsDomain: function () {
 | 
				
			||||||
 | 
								this.addingHstsDomain = this.addingHstsDomain.trim()
 | 
				
			||||||
 | 
								if (this.addingHstsDomain.length == 0) {
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (this.hstsDomainEditingIndex > -1) {
 | 
				
			||||||
 | 
									this.hsts.domains[this.hstsDomainEditingIndex] = this.addingHstsDomain
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this.hsts.domains.push(this.addingHstsDomain)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								this.cancelHstsDomainAdding()
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 取消HSTS域名添加
 | 
				
			||||||
 | 
							cancelHstsDomainAdding: function () {
 | 
				
			||||||
 | 
								this.hstsDomainAdding = false
 | 
				
			||||||
 | 
								this.addingHstsDomain = ""
 | 
				
			||||||
 | 
								this.hstsDomainEditingIndex = -1
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 删除HSTS域名
 | 
				
			||||||
 | 
							removeHstsDomain: function (index) {
 | 
				
			||||||
 | 
								this.cancelHstsDomainAdding()
 | 
				
			||||||
 | 
								this.hsts.domains.$remove(index)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 选择客户端CA证书
 | 
				
			||||||
 | 
							selectClientCACert: function () {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.popup("/servers/components/ssl/selectPopup?isCA=1", {
 | 
				
			||||||
 | 
									width: "50em",
 | 
				
			||||||
 | 
									height: "30em",
 | 
				
			||||||
 | 
									callback: function (resp) {
 | 
				
			||||||
 | 
										that.policy.clientCARefs.push(resp.data.certRef)
 | 
				
			||||||
 | 
										that.policy.clientCACerts.push(resp.data.cert)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 上传CA证书
 | 
				
			||||||
 | 
							uploadClientCACert: function () {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.popup("/servers/components/ssl/uploadPopup?isCA=1", {
 | 
				
			||||||
 | 
									height: "28em",
 | 
				
			||||||
 | 
									callback: function (resp) {
 | 
				
			||||||
 | 
										teaweb.success("上传成功", function () {
 | 
				
			||||||
 | 
											that.policy.clientCARefs.push(resp.data.certRef)
 | 
				
			||||||
 | 
											that.policy.clientCACerts.push(resp.data.cert)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 删除客户端CA证书
 | 
				
			||||||
 | 
							removeClientCACert: function (index) {
 | 
				
			||||||
 | 
								let that = this
 | 
				
			||||||
 | 
								teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前服务不再使用此证书。", function () {
 | 
				
			||||||
 | 
									that.policy.clientCARefs.$remove(index)
 | 
				
			||||||
 | 
									that.policy.clientCACerts.$remove(index)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						template: `<div>
 | 
				
			||||||
 | 
						<h4>SSL/TLS相关配置</h4>
 | 
				
			||||||
 | 
						<input type="hidden" name="sslPolicyJSON" :value="JSON.stringify(policy)"/>
 | 
				
			||||||
 | 
						<table class="ui table definition selectable">
 | 
				
			||||||
 | 
							<tbody>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td class="title">用HTTP/2</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div class="ui checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" value="1" v-model="policy.http2Enabled"/>
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td>选择证书</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div v-if="policy.certs != null && policy.certs.length > 0">
 | 
				
			||||||
 | 
											<div class="ui label small" v-for="(cert, index) in policy.certs">
 | 
				
			||||||
 | 
												{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}}   <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-else>
 | 
				
			||||||
 | 
											<span class="red">选择或上传证书后<span v-if="vProtocol == 'https'">HTTPS</span><span v-if="vProtocol == 'tls'">TLS</span>服务才能生效。</span>
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>  
 | 
				
			||||||
 | 
										<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td>TLS最低版本</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<select v-model="policy.minVersion" class="ui dropdown auto-width">
 | 
				
			||||||
 | 
											<option v-for="version in allVersions" :value="version">{{version}}</option>
 | 
				
			||||||
 | 
										</select>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</tbody>
 | 
				
			||||||
 | 
							<more-options-tbody @change="changeOptionsVisible"></more-options-tbody>
 | 
				
			||||||
 | 
							<tbody v-show="moreOptionsVisible">
 | 
				
			||||||
 | 
								<!-- 加密套件 -->
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td>加密算法套件<em>(CipherSuites)</em></td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div class="ui checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" value="1" v-model="policy.cipherSuitesIsOn" />
 | 
				
			||||||
 | 
											<label>是否要自定义</label>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-show="policy.cipherSuitesIsOn">
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
											<div class="cipher-suites-box">
 | 
				
			||||||
 | 
												已添加套件({{policy.cipherSuites.length}}):
 | 
				
			||||||
 | 
												<div v-for="cipherSuite in policy.cipherSuites" class="ui label tiny" style="margin-bottom: 0.5em">
 | 
				
			||||||
 | 
													<input type="hidden" name="cipherSuites" :value="cipherSuite"/>
 | 
				
			||||||
 | 
													<span v-html="formatCipherSuite(cipherSuite)"></span>   <a href="" title="删除套件" @click.prevent="removeCipherSuite(cipherSuite)"><i class="icon remove"></i></a>
 | 
				
			||||||
 | 
													<a href="" title="拖动改变顺序"><i class="icon bars handle"></i></a>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div>
 | 
				
			||||||
 | 
												<div class="ui divider"></div>
 | 
				
			||||||
 | 
												<span v-if="policy.cipherSuites.length > 0"><a href="" @click.prevent="clearCipherSuites()">[清除所有已选套件]</a>   </span>
 | 
				
			||||||
 | 
												<a href="" @click.prevent="addBatchCipherSuites(modernCipherSuites)">[添加推荐套件]</a>  
 | 
				
			||||||
 | 
												<a href="" @click.prevent="addBatchCipherSuites(intermediateCipherSuites)">[添加兼容套件]</a>
 | 
				
			||||||
 | 
												<div class="ui divider"></div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
											<div class="cipher-all-suites-box">
 | 
				
			||||||
 | 
												<a href="" @click.prevent="showAllCipherSuites()"><span v-if="policy.cipherSuites.length == 0">所有</span>可选套件({{allCipherSuites.length}}) <i class="icon angle" :class="{down:!cipherSuitesVisible, up:cipherSuitesVisible}"></i></a>
 | 
				
			||||||
 | 
												<a href="" v-if="cipherSuitesVisible" v-for="cipherSuite in allCipherSuites" class="ui label tiny" title="点击添加到自定义套件中" @click.prevent="addCipherSuite(cipherSuite)" v-html="formatCipherSuite(cipherSuite)" style="margin-bottom:0.5em"></a>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<p class="comment" v-if="cipherSuitesVisible">点击可选套件添加。</p>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								<!-- HSTS -->
 | 
				
			||||||
 | 
								<tr v-show="vProtocol == 'https'">
 | 
				
			||||||
 | 
									<td :class="{'color-border':hsts.isOn}">是否开启HSTS</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div class="ui checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" name="hstsOn" v-model="hsts.isOn" value="1"/>
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<p class="comment">
 | 
				
			||||||
 | 
											 开启后,会自动在响应Header中加入
 | 
				
			||||||
 | 
											 <span class="ui label small">Strict-Transport-Security:
 | 
				
			||||||
 | 
												 <var v-if="!hsts.isOn">...</var>
 | 
				
			||||||
 | 
												 <var v-if="hsts.isOn"><span>max-age=</span>{{hsts.maxAge}}</var>
 | 
				
			||||||
 | 
												 <var v-if="hsts.isOn && hsts.includeSubDomains">; includeSubDomains</var>
 | 
				
			||||||
 | 
												 <var v-if="hsts.isOn && hsts.preload">; preload</var>
 | 
				
			||||||
 | 
											 </span>
 | 
				
			||||||
 | 
											  <span v-if="hsts.isOn">
 | 
				
			||||||
 | 
												<a href="" @click.prevent="showMoreHSTS()">修改<i class="icon angle" :class="{down:!hstsOptionsVisible, up:hstsOptionsVisible}"></i> </a>
 | 
				
			||||||
 | 
											 </span>
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr v-show="hsts.isOn && hstsOptionsVisible">
 | 
				
			||||||
 | 
									<td class="color-border">HSTS包含子域名<em>(includeSubDomains)</em></td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div class="ui checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" name="hstsIncludeSubDomains" value="1" v-model="hsts.includeSubDomains"/>
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr v-show="hsts.isOn && hstsOptionsVisible">
 | 
				
			||||||
 | 
									<td class="color-border">HSTS预加载<em>(preload)</em></td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div class="ui checkbox">
 | 
				
			||||||
 | 
											<input type="checkbox" name="hstsPreload" value="1" v-model="hsts.preload"/>
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr v-show="hsts.isOn && hstsOptionsVisible">
 | 
				
			||||||
 | 
									<td class="color-border">HSTS生效的域名</td>
 | 
				
			||||||
 | 
									<td colspan="2">
 | 
				
			||||||
 | 
										<div class="names-box">
 | 
				
			||||||
 | 
										<span class="ui label tiny" v-for="(domain, arrayIndex) in hsts.domains" :class="{blue:hstsDomainEditingIndex == arrayIndex}">{{domain}}
 | 
				
			||||||
 | 
											<input type="hidden" name="hstsDomains" :value="domain"/>  
 | 
				
			||||||
 | 
											<a href="" @click.prevent="editHstsDomain(arrayIndex)" title="修改"><i class="icon pencil"></i></a>
 | 
				
			||||||
 | 
											<a href="" @click.prevent="removeHstsDomain(arrayIndex)" title="删除"><i class="icon remove"></i></a>
 | 
				
			||||||
 | 
										</span>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="ui fields inline" v-if="hstsDomainAdding" style="margin-top:0.8em">
 | 
				
			||||||
 | 
											<div class="ui field">
 | 
				
			||||||
 | 
												<input type="text" name="addingHstsDomain" ref="addingHstsDomain" style="width:16em" maxlength="100" placeholder="域名,比如example.com" @keyup.enter="confirmAddHstsDomain()" @keypress.enter.prevent="1" v-model="addingHstsDomain" />
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="ui field">
 | 
				
			||||||
 | 
												<button class="ui button tiny" type="button" @click="confirmAddHstsDomain()">确定</button>
 | 
				
			||||||
 | 
												  <a href="" @click.prevent="cancelHstsDomainAdding()">取消</a>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="ui field" style="margin-top: 1em">
 | 
				
			||||||
 | 
											<button class="ui button tiny" type="button" @click="addHstsDomain()">+</button>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<p class="comment">如果没有设置域名的话,则默认支持所有的域名。</p>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								<!-- 客户端认证 -->
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td>客户端认证方式</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<select name="clientAuthType" v-model="policy.clientAuthType" class="ui dropdown auto-width">
 | 
				
			||||||
 | 
											<option v-for="authType in allClientAuthTypes" :value="authType.type">{{authType.name}}</option>
 | 
				
			||||||
 | 
										</select>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td>客户端认证CA证书</td>
 | 
				
			||||||
 | 
									<td>
 | 
				
			||||||
 | 
										<div v-if="policy.clientCACerts != null && policy.clientCACerts.length > 0">
 | 
				
			||||||
 | 
											<div class="ui label small" v-for="(cert, index) in policy.clientCACerts">
 | 
				
			||||||
 | 
												{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}}   <a href="" title="删除" @click.prevent="removeClientCACert()"><i class="icon remove"></i></a>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<button class="ui button tiny" type="button" @click.prevent="selectClientCACert()">选择已有证书</button>  
 | 
				
			||||||
 | 
										<button class="ui button tiny" type="button" @click.prevent="uploadClientCACert()">上传新证书</button>
 | 
				
			||||||
 | 
										<p class="comment">用来校验客户端证书以增强安全性,通常不需要设置。</p>
 | 
				
			||||||
 | 
									</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</tbody>	
 | 
				
			||||||
 | 
						</table>
 | 
				
			||||||
 | 
						<div class="ui margin"></div>
 | 
				
			||||||
 | 
					</div>`
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										322
									
								
								web/public/js/date.tea.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								web/public/js/date.tea.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,322 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tea.Date 对象
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @class Tea.Date
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tea.Date构造器。使用方法如:<br/>
 | 
				
			||||||
 | 
					 * var date = new Tea.Date();<br/>
 | 
				
			||||||
 | 
					 * var date = new Tea.Date("Y-m-d H:i:s");<br/>
 | 
				
			||||||
 | 
					 * var date = new Tea.Date("Y-m-d H:i:s", 1169226085);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @constructor Tea.Date
 | 
				
			||||||
 | 
					 * @param String format 时间格式,为可选参数,目前支持O,r,Y,y,L,M,m,n,F,t,w,D,l,d,z,H,i,s,j,h,G,g,a,A等字符。
 | 
				
			||||||
 | 
					 * @param int time 时间戳,为可选参数
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Tea.Date = function (format, time) {
 | 
				
			||||||
 | 
						var date = new Date();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (typeof(format) == "undefined") {
 | 
				
			||||||
 | 
							format = "r";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (typeof(time) != "undefined") {
 | 
				
			||||||
 | 
							time = parseInt(time, 10);
 | 
				
			||||||
 | 
							date.setTime(time);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//parse char
 | 
				
			||||||
 | 
						this.get = function (chr) {
 | 
				
			||||||
 | 
							if ((chr >= "a" && chr <= "z") || (chr >= "A" && chr <= "Z")) {
 | 
				
			||||||
 | 
								var func = "_parse_" + chr;
 | 
				
			||||||
 | 
								if (this[func]) {
 | 
				
			||||||
 | 
									return this[func]();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return chr;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 根据提供的格式取得对应的时间格式
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @method parse
 | 
				
			||||||
 | 
						 * @param String format
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						this.parse = function (format) {
 | 
				
			||||||
 | 
							var result = "";
 | 
				
			||||||
 | 
							if (format.length > 0) {
 | 
				
			||||||
 | 
								for (var i=0; i<format.length; i++) {
 | 
				
			||||||
 | 
									var chr = format.charAt(i);
 | 
				
			||||||
 | 
									result += this.get(chr);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 设置某一时间为某值
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @method set
 | 
				
			||||||
 | 
						 * @param String type 时间选项,如 d 表示天,Y 表示年,H 表示小时,等等。
 | 
				
			||||||
 | 
						 * @param int value 新的值
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						this.set = function (type, value) {
 | 
				
			||||||
 | 
							value = parseInt(value, 10);
 | 
				
			||||||
 | 
							switch (type) {
 | 
				
			||||||
 | 
								case "d":
 | 
				
			||||||
 | 
									date.setDate(value);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "Y":
 | 
				
			||||||
 | 
									date.setFullYear(value);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "H":
 | 
				
			||||||
 | 
								case "G":
 | 
				
			||||||
 | 
									date.setHours(value);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "i":
 | 
				
			||||||
 | 
									date.setMinutes(value);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "s":
 | 
				
			||||||
 | 
									date.setSeconds(value);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "m":
 | 
				
			||||||
 | 
								case "n":
 | 
				
			||||||
 | 
									date.setMonth(value - 1);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//timezone
 | 
				
			||||||
 | 
						this._parse_O = function () {
 | 
				
			||||||
 | 
							var hours = (Math.abs(date.getTimezoneOffset()/60)).toString();
 | 
				
			||||||
 | 
							if (hours.length == 1) {
 | 
				
			||||||
 | 
								hours = "0" + hours;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "+" + hours + "00";
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_r = function () {
 | 
				
			||||||
 | 
							return this.parse("D, d M Y H:i:s O");
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//parse year
 | 
				
			||||||
 | 
						this._parse_Y = function () {
 | 
				
			||||||
 | 
							return date.getFullYear().toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_y = function () {
 | 
				
			||||||
 | 
							var y = this._parse_Y();
 | 
				
			||||||
 | 
							return y.substr(2);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_L = function () {
 | 
				
			||||||
 | 
							var y = parseInt(this.parse("Y"));
 | 
				
			||||||
 | 
							if (y%4 ==0  && (y%100 > 0 || y%400 == 0)) {
 | 
				
			||||||
 | 
								return "1";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "0";
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//month
 | 
				
			||||||
 | 
						this._parse_m = function () {
 | 
				
			||||||
 | 
							var n = this._parse_n();
 | 
				
			||||||
 | 
							if (n.length < 2) {
 | 
				
			||||||
 | 
								n = "0" + n;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return n;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_n = function () {
 | 
				
			||||||
 | 
							return (date.getMonth() + 1).toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_t = function () {
 | 
				
			||||||
 | 
							var t = 32 - new Date(this.get("Y"), this.get("m") - 1 , 32).getDate();
 | 
				
			||||||
 | 
							return t;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_F = function () {
 | 
				
			||||||
 | 
							var n = parseInt(this.parse("n"));
 | 
				
			||||||
 | 
							var months = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
 | 
				
			||||||
 | 
							return months[n];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_M = function () {
 | 
				
			||||||
 | 
							var n = parseInt(this.parse("n"));
 | 
				
			||||||
 | 
							var months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
 | 
				
			||||||
 | 
							return months[n];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//week
 | 
				
			||||||
 | 
						this._parse_w = function () {
 | 
				
			||||||
 | 
							return date.getDay().toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_D = function () {
 | 
				
			||||||
 | 
							var w = parseInt(this._parse_w());
 | 
				
			||||||
 | 
							var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
 | 
				
			||||||
 | 
							return days[w];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_l = function () {
 | 
				
			||||||
 | 
							var w = parseInt(this._parse_w());
 | 
				
			||||||
 | 
							var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
 | 
				
			||||||
 | 
							return days[w];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//day
 | 
				
			||||||
 | 
						this._parse_d = function () {
 | 
				
			||||||
 | 
							var j = this._parse_j();
 | 
				
			||||||
 | 
							if (j.length < 2) {
 | 
				
			||||||
 | 
								j = "0" + j;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return j;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_j = function () {
 | 
				
			||||||
 | 
							return date.getDate().toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_W = function () {
 | 
				
			||||||
 | 
							var _date = new Tea.Date();
 | 
				
			||||||
 | 
							_date.set("m", 1);
 | 
				
			||||||
 | 
							_date.set("d", 1);
 | 
				
			||||||
 | 
							var w = parseInt(_date.parse("w"));
 | 
				
			||||||
 | 
							var m = parseInt(this.parse("m"), 10);
 | 
				
			||||||
 | 
							var total = 0;
 | 
				
			||||||
 | 
							for (var i=1; i<m; i++) {
 | 
				
			||||||
 | 
								var date2 = new Tea.Date();
 | 
				
			||||||
 | 
								date2.set("m", i);
 | 
				
			||||||
 | 
								var t = parseInt(date2.parse("t"));
 | 
				
			||||||
 | 
								total += t;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							total += parseInt(this.parse("d"), 10);
 | 
				
			||||||
 | 
							var w2 = parseInt(this.parse("w"));
 | 
				
			||||||
 | 
							total = total - w2 + (w - 1);
 | 
				
			||||||
 | 
							var weeks = 0;
 | 
				
			||||||
 | 
							if (w2 != 0) {
 | 
				
			||||||
 | 
								weeks = (total/7 + 1).toString();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								weeks = (total/7).toString();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (weeks.length == 1) {
 | 
				
			||||||
 | 
								weeks = "0" + weeks;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return weeks;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_z = function () {
 | 
				
			||||||
 | 
							var m = parseInt(this.parse("m"), 10);
 | 
				
			||||||
 | 
							var total = 0;
 | 
				
			||||||
 | 
							for (var i=1; i<m; i++) {
 | 
				
			||||||
 | 
								var date2 = new Tea.Date();
 | 
				
			||||||
 | 
								date2.set("m", i);
 | 
				
			||||||
 | 
								var t = parseInt(date2.parse("t"));
 | 
				
			||||||
 | 
								total += t;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							total += parseInt(this.parse("d"), 10) - 1;
 | 
				
			||||||
 | 
							return total;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//minute
 | 
				
			||||||
 | 
						this._parse_i = function () {
 | 
				
			||||||
 | 
							var i = date.getMinutes().toString();
 | 
				
			||||||
 | 
							if (i.length < 2) {
 | 
				
			||||||
 | 
								i = "0" + i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return i;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//second
 | 
				
			||||||
 | 
						this._parse_s = function () {
 | 
				
			||||||
 | 
							var s = date.getSeconds().toString();
 | 
				
			||||||
 | 
							if (s.length < 2) {
 | 
				
			||||||
 | 
								s = "0" + s;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return s;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//hour
 | 
				
			||||||
 | 
						this._parse_H = function () {
 | 
				
			||||||
 | 
							var H = this._parse_G();
 | 
				
			||||||
 | 
							if (H.length < 2) {
 | 
				
			||||||
 | 
								H = "0" + H;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return H;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_G = function () {
 | 
				
			||||||
 | 
							return date.getHours().toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_h = function () {
 | 
				
			||||||
 | 
							var h = this._parse_g();
 | 
				
			||||||
 | 
							if (h.length < 2) {
 | 
				
			||||||
 | 
								h = "0" + h;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return h;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_g = function () {
 | 
				
			||||||
 | 
							var g = parseInt(this._parse_G(), 10);
 | 
				
			||||||
 | 
							if (g > 12) {
 | 
				
			||||||
 | 
								g = g - 12;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return g.toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//time
 | 
				
			||||||
 | 
						this._parse_U = function () {
 | 
				
			||||||
 | 
							return this.time().toString();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//am/pm
 | 
				
			||||||
 | 
						this._parse_a = function () {
 | 
				
			||||||
 | 
							var hour = this.parse("H");
 | 
				
			||||||
 | 
							return (hour<12)?"am":"pm";
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this._parse_A = function () {
 | 
				
			||||||
 | 
							return this.parse("a").toUpperCase();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 取得当前时间对应的时间戳,代表了从 1970 年 1 月 1 日开始计算到 Date 对象中的时间之间的秒数
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @method time
 | 
				
			||||||
 | 
						 * @return int
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						this.time = function () {
 | 
				
			||||||
 | 
							return Math.round(date.getTime()/1000);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * 将该对象转换成字符串格式
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @method toString
 | 
				
			||||||
 | 
						 * @return String 该对象的字符串表示形式
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						this.toString = function () {
 | 
				
			||||||
 | 
							return this.parse(format);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tea.Date.toTime = function (dateStr) {
 | 
				
			||||||
 | 
						if (arguments.length == 1) {
 | 
				
			||||||
 | 
							return Date.parse(dateStr);
 | 
				
			||||||
 | 
						} else if (arguments.length == 3) {
 | 
				
			||||||
 | 
							arguments[1] = parseInt(arguments[1], 10) - 1;
 | 
				
			||||||
 | 
							return (new Date(arguments[0], arguments[1], arguments[2])).time();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Number.prototype.dateFormat = function (format) {
 | 
				
			||||||
 | 
						var date = new Tea.Date(format, this * 1000);
 | 
				
			||||||
 | 
						return date.toString();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Date.prototype.format = function (format) {
 | 
				
			||||||
 | 
						return new Tea.Date(format, this.getTime()).toString();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -15,6 +15,7 @@
 | 
				
			|||||||
	<script type="text/javascript" src="/ui/components.js?v=1.0.0"></script>
 | 
						<script type="text/javascript" src="/ui/components.js?v=1.0.0"></script>
 | 
				
			||||||
	<script type="text/javascript" src="/js/utils.js"></script>
 | 
						<script type="text/javascript" src="/js/utils.js"></script>
 | 
				
			||||||
	<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
 | 
						<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
 | 
				
			||||||
 | 
						<script type="text/javascript" src="/js/date.tea.js"></script>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,11 @@
 | 
				
			|||||||
			</tr>
 | 
								</tr>
 | 
				
			||||||
		</thead>
 | 
							</thead>
 | 
				
			||||||
		<tr v-for="(cert, index) in certs">
 | 
							<tr v-for="(cert, index) in certs">
 | 
				
			||||||
			<td>{{cert.name}}</td>
 | 
								<td>{{cert.name}}
 | 
				
			||||||
 | 
									<div v-if="cert.isCA" style="margin-top:0.5em">
 | 
				
			||||||
 | 
										<span class="ui label olive tiny">CA</span>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</td>
 | 
				
			||||||
			<td>
 | 
								<td>
 | 
				
			||||||
				<span v-if="cert.commonNames != null && cert.commonNames.length > 0">{{cert.commonNames[cert.commonNames.length-1]}}</span>
 | 
									<span v-if="cert.commonNames != null && cert.commonNames.length > 0">{{cert.commonNames[cert.commonNames.length-1]}}</span>
 | 
				
			||||||
			</td>
 | 
								</td>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										43
									
								
								web/views/@default/servers/components/ssl/selectPopup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								web/views/@default/servers/components/ssl/selectPopup.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					{$layout "layout_popup"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<h3>选择证书</h3>
 | 
				
			||||||
 | 
					<p class="comment" v-if="certs.length == 0">暂时还没有相关的证书。</p>
 | 
				
			||||||
 | 
					<table class="ui table selectable" v-if="certs.length > 0">
 | 
				
			||||||
 | 
						<thead>
 | 
				
			||||||
 | 
						<tr>
 | 
				
			||||||
 | 
							<th>证书说明</th>
 | 
				
			||||||
 | 
							<th>顶级发行组织</th>
 | 
				
			||||||
 | 
							<th>域名</th>
 | 
				
			||||||
 | 
							<th>过期日期</th>
 | 
				
			||||||
 | 
							<th>引用服务</th>
 | 
				
			||||||
 | 
							<th>状态</th>
 | 
				
			||||||
 | 
							<th class="one op">操作</th>
 | 
				
			||||||
 | 
						</tr>
 | 
				
			||||||
 | 
						</thead>
 | 
				
			||||||
 | 
						<tr v-for="(cert, index) in certs">
 | 
				
			||||||
 | 
							<td>{{cert.name}}
 | 
				
			||||||
 | 
								<div v-if="cert.isCA" style="margin-top:0.5em">
 | 
				
			||||||
 | 
									<span class="ui label olive tiny">CA</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</td>
 | 
				
			||||||
 | 
							<td>
 | 
				
			||||||
 | 
								<span v-if="cert.commonNames != null && cert.commonNames.length > 0">{{cert.commonNames[cert.commonNames.length-1]}}</span>
 | 
				
			||||||
 | 
							</td>
 | 
				
			||||||
 | 
							<td>
 | 
				
			||||||
 | 
								<div v-for="dnsName in cert.dnsNames" style="margin-bottom:0.4em">
 | 
				
			||||||
 | 
									<span class="ui label tiny">{{dnsName}}</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</td>
 | 
				
			||||||
 | 
							<td>{{certInfos[index].endDay}}</td>
 | 
				
			||||||
 | 
							<td>{{certInfos[index].countServers}}</td>
 | 
				
			||||||
 | 
							<td nowrap="">
 | 
				
			||||||
 | 
								<span class="ui label red tiny basic" v-if="certInfos[index].isExpired">已过期</span>
 | 
				
			||||||
 | 
								<span class="ui label green tiny basic" v-else>有效中</span>
 | 
				
			||||||
 | 
							</td>
 | 
				
			||||||
 | 
							<td>
 | 
				
			||||||
 | 
								<a href="" @click.prevent="selectCert(cert)">选择</a>
 | 
				
			||||||
 | 
							</td>
 | 
				
			||||||
 | 
						</tr>
 | 
				
			||||||
 | 
					</table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="page" v-html="page"></div>
 | 
				
			||||||
							
								
								
									
										14
									
								
								web/views/@default/servers/components/ssl/selectPopup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								web/views/@default/servers/components/ssl/selectPopup.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					Tea.context(function () {
 | 
				
			||||||
 | 
						this.selectCert = function (cert) {
 | 
				
			||||||
 | 
							NotifyPopup({
 | 
				
			||||||
 | 
								code: 200,
 | 
				
			||||||
 | 
								data: {
 | 
				
			||||||
 | 
									cert: cert,
 | 
				
			||||||
 | 
									certRef: {
 | 
				
			||||||
 | 
										isOn: true,
 | 
				
			||||||
 | 
										certId: cert.id
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@@ -1,7 +1,11 @@
 | 
				
			|||||||
{$layout}
 | 
					{$layout}
 | 
				
			||||||
 | 
					 | 
				
			||||||
{$template "/left_menu"}
 | 
					{$template "/left_menu"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{$var "header"}
 | 
				
			||||||
 | 
						<script src="/servers/components/ssl/datajs" type="text/javascript"></script>
 | 
				
			||||||
 | 
						<script src="/js/sortable.min.js" type="text/javascript"></script>
 | 
				
			||||||
 | 
					{$end}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="right-box">
 | 
					<div class="right-box">
 | 
				
			||||||
	<p class="comment">提醒:HTTP2、证书等信息修改后,可能需要清空浏览器缓存后才能浏览效果。</p>
 | 
						<p class="comment">提醒:HTTP2、证书等信息修改后,可能需要清空浏览器缓存后才能浏览效果。</p>
 | 
				
			||||||
	<form class="ui form" data-tea-action="$" data-tea-success="success">
 | 
						<form class="ui form" data-tea-action="$" data-tea-success="success">
 | 
				
			||||||
@@ -26,6 +30,10 @@
 | 
				
			|||||||
				</tr>
 | 
									</tr>
 | 
				
			||||||
			</tbody>
 | 
								</tbody>
 | 
				
			||||||
		</table>
 | 
							</table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<!-- SSL配置 -->
 | 
				
			||||||
 | 
							<ssl-config-box :v-ssl-policy="httpsConfig.sslPolicy" :v-protocol="'https'" v-show="httpsConfig.isOn"></ssl-config-box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<submit-btn></submit-btn>
 | 
							<submit-btn></submit-btn>
 | 
				
			||||||
	</form>
 | 
						</form>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user