diff --git a/api/certificate/certificate.go b/api/certificate/certificate.go index 23518da2..7ebaca29 100644 --- a/api/certificate/certificate.go +++ b/api/certificate/certificate.go @@ -131,6 +131,31 @@ func AddCert(c *gin.Context) { return } + // Detect and set certificate type + if len(json.SSLCertificate) > 0 { + keyType, err := cert.GetKeyType(json.SSLCertificate) + if err == nil && keyType != "" { + // Set KeyType based on certificate type + switch keyType { + case "2048": + certModel.KeyType = certcrypto.RSA2048 + case "3072": + certModel.KeyType = certcrypto.RSA3072 + case "4096": + certModel.KeyType = certcrypto.RSA4096 + case "P256": + certModel.KeyType = certcrypto.EC256 + case "P384": + certModel.KeyType = certcrypto.EC384 + } + // Update certificate model + err = certModel.Updates(&model.Cert{KeyType: certModel.KeyType}) + if err != nil { + notification.Error("Update Certificate Type Error", err.Error(), nil) + } + } + } + err = cert.SyncToRemoteServer(certModel) if err != nil { notification.Error("Sync Certificate Error", err.Error(), nil) @@ -157,7 +182,8 @@ func ModifyCert(c *gin.Context) { return } - err = certModel.Updates(&model.Cert{ + // Create update data object + updateData := &model.Cert{ Name: json.Name, SSLCertificatePath: json.SSLCertificatePath, SSLCertificateKeyPath: json.SSLCertificateKeyPath, @@ -166,11 +192,6 @@ func ModifyCert(c *gin.Context) { DnsCredentialID: json.DnsCredentialID, ACMEUserID: json.ACMEUserID, SyncNodeIds: json.SyncNodeIds, - }) - - if err != nil { - cosy.ErrHandler(c, err) - return } content := &cert.Content{ @@ -186,6 +207,32 @@ func ModifyCert(c *gin.Context) { return } + // Detect and set certificate type + if len(json.SSLCertificate) > 0 { + keyType, err := cert.GetKeyType(json.SSLCertificate) + if err == nil && keyType != "" { + // Set KeyType based on certificate type + switch keyType { + case "2048": + updateData.KeyType = certcrypto.RSA2048 + case "3072": + updateData.KeyType = certcrypto.RSA3072 + case "4096": + updateData.KeyType = certcrypto.RSA4096 + case "P256": + updateData.KeyType = certcrypto.EC256 + case "P384": + updateData.KeyType = certcrypto.EC384 + } + } + } + + err = certModel.Updates(updateData) + if err != nil { + cosy.ErrHandler(c, err) + return + } + err = cert.SyncToRemoteServer(certModel) if err != nil { notification.Error("Sync Certificate Error", err.Error(), nil) diff --git a/app/src/views/certificate/CertificateList/certColumns.tsx b/app/src/views/certificate/CertificateList/certColumns.tsx index 6acb1d9b..b8d6d2e5 100644 --- a/app/src/views/certificate/CertificateList/certColumns.tsx +++ b/app/src/views/certificate/CertificateList/certColumns.tsx @@ -33,27 +33,24 @@ const columns: Column[] = [{ if (text === true || text === 1) { template.push( - { managed } + {managed} , ) } else if (text === 2) { template.push( - { sync } + {sync} , ) } else { template.push( - { - general - } + {general} , ) } - return h('div', template) }, sorter: true, diff --git a/app/src/views/site/site_list/SiteList.vue b/app/src/views/site/site_list/SiteList.vue index fe543eb8..cc60cab2 100644 --- a/app/src/views/site/site_list/SiteList.vue +++ b/app/src/views/site/site_list/SiteList.vue @@ -164,7 +164,7 @@ function handleBatchUpdated() { :get-params="{ env_group_id: envGroupId, }" - :scroll-x="1200" + :scroll-x="1600" @click-edit="(r: string) => router.push({ path: `/sites/${r}`, })" diff --git a/app/src/views/stream/StreamList.vue b/app/src/views/stream/StreamList.vue index ce8d1ecc..b62fc35e 100644 --- a/app/src/views/stream/StreamList.vue +++ b/app/src/views/stream/StreamList.vue @@ -24,6 +24,7 @@ const columns: Column[] = [{ type: input, }, search: true, + width: 150, }, { title: () => $gettext('Node Group'), dataIndex: 'env_group_id', diff --git a/internal/cert/errors.go b/internal/cert/errors.go index 2a700d8d..480bf755 100644 --- a/internal/cert/errors.go +++ b/internal/cert/errors.go @@ -10,4 +10,5 @@ var ( ErrCertParse = e.New(50004, "certificate parse error") ErrPayloadResourceIsNil = e.New(50005, "payload resource is nil") ErrPathIsNotUnderTheNginxConfDir = e.New(50006, "path: {0} is not under the nginx conf dir: {1}") + ErrCertPathIsEmpty = e.New(50007, "certificate path is empty") ) diff --git a/internal/cert/helper.go b/internal/cert/helper.go index de93e995..919e5fb2 100644 --- a/internal/cert/helper.go +++ b/internal/cert/helper.go @@ -1,6 +1,8 @@ package cert import ( + "crypto/ecdsa" + "crypto/rsa" "crypto/x509" "encoding/pem" "os" @@ -79,3 +81,67 @@ func IsPrivateKeyPath(path string) bool { return IsPrivateKey(string(bytes)) } + +// GetKeyType determines the key type from a PEM certificate string. +// Returns "2048", "3072", "4096", "P256", "P384" or empty string. +func GetKeyType(pemStr string) (string, error) { + block, _ := pem.Decode([]byte(pemStr)) + if block == nil { + return "", ErrCertDecode + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return "", ErrCertParse + } + + switch cert.PublicKeyAlgorithm { + case x509.RSA: + rsaKey, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return "", nil + } + keySize := rsaKey.Size() * 8 // Size returns size in bytes, convert to bits + switch keySize { + case 2048: + return "2048", nil + case 3072: + return "3072", nil + case 4096: + return "4096", nil + default: + return "", nil + } + case x509.ECDSA: + ecKey, ok := cert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return "", nil + } + curve := ecKey.Curve.Params().Name + switch curve { + case "P-256": + return "P256", nil + case "P-384": + return "P384", nil + default: + return "", nil + } + default: + return "", nil + } +} + +// GetKeyTypeFromPath determines the key type from a certificate file. +// Returns "2048", "3072", "4096", "P256", "P384" or empty string. +func GetKeyTypeFromPath(path string) (string, error) { + if path == "" { + return "", ErrCertPathIsEmpty + } + + bytes, err := os.ReadFile(path) + if err != nil { + return "", err + } + + return GetKeyType(string(bytes)) +}