Procházet zdrojové kódy

enhance: show the form error details of CertificateEditor

0xJacky před 1 rokem
rodič
revize
b0a3989ef4

+ 141 - 141
api/certificate/certificate.go

@@ -1,174 +1,174 @@
 package certificate
 
 import (
-    "github.com/0xJacky/Nginx-UI/api"
-    "github.com/0xJacky/Nginx-UI/api/cosy"
-    "github.com/0xJacky/Nginx-UI/internal/cert"
-    "github.com/0xJacky/Nginx-UI/model"
-    "github.com/0xJacky/Nginx-UI/query"
-    "github.com/gin-gonic/gin"
-    "github.com/spf13/cast"
-    "net/http"
-    "os"
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/api/cosy"
+	"github.com/0xJacky/Nginx-UI/internal/cert"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/cast"
+	"net/http"
+	"os"
 )
 
 type APICertificate struct {
-    *model.Cert
-    SSLCertificate    string     `json:"ssl_certificate,omitempty"`
-    SSLCertificateKey string     `json:"ssl_certificate_key,omitempty"`
-    CertificateInfo   *cert.Info `json:"certificate_info,omitempty"`
+	*model.Cert
+	SSLCertificate    string     `json:"ssl_certificate,omitempty"`
+	SSLCertificateKey string     `json:"ssl_certificate_key,omitempty"`
+	CertificateInfo   *cert.Info `json:"certificate_info,omitempty"`
 }
 
 func Transformer(certModel *model.Cert) (certificate *APICertificate) {
-    var sslCertificationBytes, sslCertificationKeyBytes []byte
-    var certificateInfo *cert.Info
-    if certModel.SSLCertificatePath != "" {
-        if _, err := os.Stat(certModel.SSLCertificatePath); err == nil {
-            sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath)
-            if !cert.IsPublicKey(string(sslCertificationBytes)) {
-                sslCertificationBytes = []byte{}
-            }
-        }
-
-        certificateInfo, _ = cert.GetCertInfo(certModel.SSLCertificatePath)
-    }
-
-    if certModel.SSLCertificateKeyPath != "" {
-        if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil {
-            sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath)
-            if !cert.IsPrivateKey(string(sslCertificationKeyBytes)) {
-                sslCertificationKeyBytes = []byte{}
-            }
-        }
-    }
-
-    return &APICertificate{
-        Cert:              certModel,
-        SSLCertificate:    string(sslCertificationBytes),
-        SSLCertificateKey: string(sslCertificationKeyBytes),
-        CertificateInfo:   certificateInfo,
-    }
+	var sslCertificationBytes, sslCertificationKeyBytes []byte
+	var certificateInfo *cert.Info
+	if certModel.SSLCertificatePath != "" {
+		if _, err := os.Stat(certModel.SSLCertificatePath); err == nil {
+			sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath)
+			if !cert.IsPublicKey(string(sslCertificationBytes)) {
+				sslCertificationBytes = []byte{}
+			}
+		}
+
+		certificateInfo, _ = cert.GetCertInfo(certModel.SSLCertificatePath)
+	}
+
+	if certModel.SSLCertificateKeyPath != "" {
+		if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil {
+			sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath)
+			if !cert.IsPrivateKey(string(sslCertificationKeyBytes)) {
+				sslCertificationKeyBytes = []byte{}
+			}
+		}
+	}
+
+	return &APICertificate{
+		Cert:              certModel,
+		SSLCertificate:    string(sslCertificationBytes),
+		SSLCertificateKey: string(sslCertificationKeyBytes),
+		CertificateInfo:   certificateInfo,
+	}
 }
 
 func GetCertList(c *gin.Context) {
-    cosy.Core[model.Cert](c).SetFussy("name", "domain").SetTransformer(func(m *model.Cert) any {
+	cosy.Core[model.Cert](c).SetFussy("name", "domain").SetTransformer(func(m *model.Cert) any {
 
-        info, _ := cert.GetCertInfo(m.SSLCertificatePath)
+		info, _ := cert.GetCertInfo(m.SSLCertificatePath)
 
-        return APICertificate{
-            Cert:            m,
-            CertificateInfo: info,
-        }
-    }).PagingList()
+		return APICertificate{
+			Cert:            m,
+			CertificateInfo: info,
+		}
+	}).PagingList()
 }
 
 func GetCert(c *gin.Context) {
-    q := query.Cert
+	q := query.Cert
 
-    certModel, err := q.FirstByID(cast.ToInt(c.Param("id")))
+	certModel, err := q.FirstByID(cast.ToInt(c.Param("id")))
 
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
 
-    c.JSON(http.StatusOK, Transformer(certModel))
+	c.JSON(http.StatusOK, Transformer(certModel))
 }
 
 type certJson struct {
-    Name                  string `json:"name"`
-    SSLCertificatePath    string `json:"ssl_certificate_path" binding:"publickey_path"`
-    SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"privatekey_path"`
-    SSLCertificate        string `json:"ssl_certificate" binding:"omitempty,publickey"`
-    SSLCertificateKey     string `json:"ssl_certificate_key" binding:"omitempty,privatekey"`
-    ChallengeMethod       string `json:"challenge_method"`
-    DnsCredentialID       int    `json:"dns_credential_id"`
+	Name                  string `json:"name" binding:"required"`
+	SSLCertificatePath    string `json:"ssl_certificate_path" binding:"required,publickey_path"`
+	SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required,privatekey_path"`
+	SSLCertificate        string `json:"ssl_certificate" binding:"omitempty,publickey"`
+	SSLCertificateKey     string `json:"ssl_certificate_key" binding:"omitempty,privatekey"`
+	ChallengeMethod       string `json:"challenge_method"`
+	DnsCredentialID       int    `json:"dns_credential_id"`
 }
 
 func AddCert(c *gin.Context) {
-    var json certJson
-    if !api.BindAndValid(c, &json) {
-        return
-    }
-    certModel := &model.Cert{
-        Name:                  json.Name,
-        SSLCertificatePath:    json.SSLCertificatePath,
-        SSLCertificateKeyPath: json.SSLCertificateKeyPath,
-        ChallengeMethod:       json.ChallengeMethod,
-        DnsCredentialID:       json.DnsCredentialID,
-    }
-
-    err := certModel.Insert()
-
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
-
-    content := &cert.Content{
-        SSLCertificatePath:    json.SSLCertificatePath,
-        SSLCertificateKeyPath: json.SSLCertificateKeyPath,
-        SSLCertificate:        json.SSLCertificate,
-        SSLCertificateKey:     json.SSLCertificateKey,
-    }
-
-    err = content.WriteFile()
-
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
-
-    c.JSON(http.StatusOK, Transformer(certModel))
+	var json certJson
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+	certModel := &model.Cert{
+		Name:                  json.Name,
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		ChallengeMethod:       json.ChallengeMethod,
+		DnsCredentialID:       json.DnsCredentialID,
+	}
+
+	err := certModel.Insert()
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	content := &cert.Content{
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		SSLCertificate:        json.SSLCertificate,
+		SSLCertificateKey:     json.SSLCertificateKey,
+	}
+
+	err = content.WriteFile()
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, Transformer(certModel))
 }
 
 func ModifyCert(c *gin.Context) {
-    id := cast.ToInt(c.Param("id"))
-
-    var json certJson
-
-    if !api.BindAndValid(c, &json) {
-        return
-    }
-
-    q := query.Cert
-
-    certModel, err := q.FirstByID(id)
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
-
-    err = certModel.Updates(&model.Cert{
-        Name:                  json.Name,
-        SSLCertificatePath:    json.SSLCertificatePath,
-        SSLCertificateKeyPath: json.SSLCertificateKeyPath,
-        ChallengeMethod:       json.ChallengeMethod,
-        DnsCredentialID:       json.DnsCredentialID,
-    })
-
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
-
-    content := &cert.Content{
-        SSLCertificatePath:    json.SSLCertificatePath,
-        SSLCertificateKeyPath: json.SSLCertificateKeyPath,
-        SSLCertificate:        json.SSLCertificate,
-        SSLCertificateKey:     json.SSLCertificateKey,
-    }
-
-    err = content.WriteFile()
-
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
-
-    GetCert(c)
+	id := cast.ToInt(c.Param("id"))
+
+	var json certJson
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	q := query.Cert
+
+	certModel, err := q.FirstByID(id)
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	err = certModel.Updates(&model.Cert{
+		Name:                  json.Name,
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		ChallengeMethod:       json.ChallengeMethod,
+		DnsCredentialID:       json.DnsCredentialID,
+	})
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	content := &cert.Content{
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		SSLCertificate:        json.SSLCertificate,
+		SSLCertificateKey:     json.SSLCertificateKey,
+	}
+
+	err = content.WriteFile()
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	GetCert(c)
 }
 
 func RemoveCert(c *gin.Context) {
-    cosy.Core[model.Cert](c).Destroy()
+	cosy.Core[model.Cert](c).Destroy()
 }

+ 1 - 1
app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "nginx-ui-app-next",
-  "version": "2.0.0-beta.11",
+  "version": "2.0.0-beta.12",
   "type": "module",
   "scripts": {
     "dev": "vite",

+ 36 - 5
app/src/views/certificate/CertificateEditor.vue

@@ -41,12 +41,15 @@ onMounted(() => {
 })
 
 const router = useRouter()
+const errors = ref({}) as Ref<Record<string, string>>
 function save() {
   cert.save(data.value.id, data.value).then(r => {
     data.value = r
     message.success($gettext('Save successfully'))
     router.push(`/certificates/${r.id}`)
+    errors.value = {}
   }).catch(e => {
+    errors.value = e.errors
     message.error($gettext(e?.message ?? 'Server error'))
   })
 }
@@ -142,7 +145,13 @@ const isManaged = computed(() => {
           layout="vertical"
           style="max-width: 600px"
         >
-          <AFormItem :label="$gettext('Name')">
+          <AFormItem
+            :label="$gettext('Name')"
+            :validate-status="errors.name ? 'error' : ''"
+            :help="errors.name === 'required'
+              ? $gettext('This field is required')
+              : ''"
+          >
             <p v-if="isManaged">
               {{ data.name }}
             </p>
@@ -151,7 +160,13 @@ const isManaged = computed(() => {
               v-model:value="data.name"
             />
           </AFormItem>
-          <AFormItem :label="$gettext('SSL Certificate Path')">
+          <AFormItem
+            :label="$gettext('SSL Certificate Path')"
+            :validate-status="errors.ssl_certificate_path ? 'error' : ''"
+            :help="errors.ssl_certificate_path === 'required' ? $gettext('This field is required')
+              : errors.ssl_certificate_path === 'publickey_path'
+                ? $gettext('The path exists, but the file is not a public key') : ''"
+          >
             <p v-if="isManaged">
               {{ data.ssl_certificate_path }}
             </p>
@@ -160,7 +175,13 @@ const isManaged = computed(() => {
               v-model:value="data.ssl_certificate_path"
             />
           </AFormItem>
-          <AFormItem :label="$gettext('SSL Certificate Key Path')">
+          <AFormItem
+            :label="$gettext('SSL Certificate Key Path')"
+            :validate-status="errors.ssl_certificate_key_path ? 'error' : ''"
+            :help="errors.ssl_certificate_key_path === 'required' ? $gettext('This field is required')
+              : errors.ssl_certificate_key_path === 'privatekey_path'
+                ? $gettext('The path exists, but the file is not a private key') : ''"
+          >
             <p v-if="isManaged">
               {{ data.ssl_certificate_key_path }}
             </p>
@@ -169,7 +190,12 @@ const isManaged = computed(() => {
               v-model:value="data.ssl_certificate_key_path"
             />
           </AFormItem>
-          <AFormItem :label="$gettext('SSL Certificate Content')">
+          <AFormItem
+            :label="$gettext('SSL Certificate Content')"
+            :validate-status="errors.ssl_certificate ? 'error' : ''"
+            :help="errors.ssl_certificate === 'publickey'
+              ? $gettext('The input is not a SSL Certificate') : ''"
+          >
             <CodeEditor
               v-model:content="data.ssl_certificate"
               default-height="300px"
@@ -177,7 +203,12 @@ const isManaged = computed(() => {
               :placeholder="$gettext('Leave blank will not change anything')"
             />
           </AFormItem>
-          <AFormItem :label="$gettext('SSL Certificate Key Content')">
+          <AFormItem
+            :label="$gettext('SSL Certificate Key Content')"
+            :validate-status="errors.ssl_certificate_key ? 'error' : ''"
+            :help="errors.ssl_certificate_key === 'privatekey'
+              ? $gettext('The input is not a SSL Certificate Key') : ''"
+          >
             <CodeEditor
               v-model:content="data.ssl_certificate_key"
               default-height="300px"

+ 7 - 0
internal/cert/helper.go

@@ -33,6 +33,9 @@ func IsPrivateKey(pemStr string) bool {
 
 // IsPublicKeyPath checks if the file at the given path is a public key or not exists.
 func IsPublicKeyPath(path string) bool {
+	if path == "" {
+		return false
+	}
 	_, err := os.Stat(path)
 
 	if err != nil {
@@ -52,6 +55,10 @@ func IsPublicKeyPath(path string) bool {
 
 // IsPrivateKeyPath checks if the file at the given path is a private key or not exists.
 func IsPrivateKeyPath(path string) bool {
+	if path == "" {
+		return false
+	}
+
 	_, err := os.Stat(path)
 
 	if err != nil {