1
0
Эх сурвалжийг харах

enhance: show the form error details of Preference

0xJacky 1 жил өмнө
parent
commit
398eea2159

+ 4 - 4
api/api.go

@@ -38,18 +38,18 @@ func BindAndValid(c *gin.Context, target interface{}) bool {
 			return false
 		}
 
-		t := reflect.TypeOf(target)
+		t := reflect.TypeOf(target).Elem()
 		errorsMap := make(map[string]interface{})
 		for _, value := range verrs {
 			var path []string
 
 			namespace := strings.Split(value.StructNamespace(), ".")
-
-			if t.Name() == "" && len(namespace) > 1 {
+			logger.Debug(t.Name(), namespace)
+			if t.Name() != "" && len(namespace) > 1 {
 				namespace = namespace[1:]
 			}
 
-			getJsonPath(t.Elem(), namespace, &path)
+			getJsonPath(t, namespace, &path)
 			insertError(errorsMap, path, value.Tag())
 		}
 

+ 2 - 0
api/certificate/certificate.go

@@ -87,9 +87,11 @@ type certJson struct {
 
 func AddCert(c *gin.Context) {
 	var json certJson
+
 	if !api.BindAndValid(c, &json) {
 		return
 	}
+
 	certModel := &model.Cert{
 		Name:                  json.Name,
 		SSLCertificatePath:    json.SSLCertificatePath,

+ 0 - 1
api/system/settings.go

@@ -27,7 +27,6 @@ func SaveSettings(c *gin.Context) {
 		return
 	}
 
-	// todo: omit protected fields when binding
 	fillSettings(&settings.ServerSettings, &json.Server)
 	fillSettings(&settings.NginxSettings, &json.Nginx)
 	fillSettings(&settings.OpenAISettings, &json.Openai)

+ 18 - 5
app/src/views/preference/BasicSettings.vue

@@ -1,10 +1,11 @@
 <script setup lang="ts">
 import { useGettext } from 'vue3-gettext'
 import { inject } from 'vue'
-import type { IData } from '@/views/preference/typedef'
+import type { Settings } from '@/views/preference/typedef'
 
 const { $gettext } = useGettext()
-const data: IData = inject('data') as IData
+const data: Settings = inject('data') as Settings
+const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>
 </script>
 
 <template>
@@ -30,13 +31,25 @@ const data: IData = inject('data') as IData
     <AFormItem :label="$gettext('HTTP Challenge Port')">
       <AInputNumber v-model:value="data.server.http_challenge_port" />
     </AFormItem>
-    <AFormItem :label="$gettext('Github Proxy')">
+    <AFormItem
+      :label="$gettext('Github Proxy')"
+      :validate-status="errors?.server?.github_proxy ? 'error' : ''"
+      :help="errors?.server?.github_proxy === 'url'
+        ? $gettext('The url is not valid')
+        : ''"
+    >
       <AInput
         v-model:value="data.server.github_proxy"
-        :placeholder="$gettext('Chinese user: https://mirror.ghproxy.com/')"
+        :placeholder="$gettext('For Chinese user: https://mirror.ghproxy.com/')"
       />
     </AFormItem>
-    <AFormItem :label="$gettext('CADir')">
+    <AFormItem
+      :label="$gettext('CADir')"
+      :validate-status="errors?.server?.ca_dir ? 'error' : ''"
+      :help="errors?.server?.ca_dir === 'url'
+        ? $gettext('The url is not valid')
+        : ''"
+    >
       <AInput v-model:value="data.server.ca_dir" />
     </AFormItem>
   </AForm>

+ 17 - 4
app/src/views/preference/NginxSettings.vue

@@ -1,19 +1,32 @@
 <script setup lang="ts">
 import { useGettext } from 'vue3-gettext'
 import { inject } from 'vue'
-import type { IData } from '@/views/preference/typedef'
+import type { Settings } from '@/views/preference/typedef'
 
 const { $gettext } = useGettext()
 
-const data: IData = inject('data')!
+const data: Settings = inject('data')!
+const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>
 </script>
 
 <template>
   <AForm layout="vertical">
-    <AFormItem :label="$gettext('Nginx Access Log Path')">
+    <AFormItem
+      :label="$gettext('Nginx Access Log Path')"
+      :validate-status="errors?.nginx?.access_log_path ? 'error' : ''"
+      :help="errors?.nginx?.access_log_path === 'file'
+        ? $gettext('File not found')
+        : ''"
+    >
       <AInput v-model:value="data.nginx.access_log_path" />
     </AFormItem>
-    <AFormItem :label="$gettext('Nginx Error Log Path')">
+    <AFormItem
+      :label="$gettext('Nginx Error Log Path')"
+      :validate-status="errors?.nginx?.error_log_path ? 'error' : ''"
+      :help="errors?.nginx?.error_log_path === 'file'
+        ? $gettext('File not found')
+        : ''"
+    >
       <AInput v-model:value="data.nginx.error_log_path" />
     </AFormItem>
   </AForm>

+ 24 - 5
app/src/views/preference/OpenAISettings.vue

@@ -1,11 +1,12 @@
 <script setup lang="ts">
 import { useGettext } from 'vue3-gettext'
 import { inject } from 'vue'
-import type { IData } from '@/views/preference/typedef'
+import type { Settings } from '@/views/preference/typedef'
 
 const { $gettext } = useGettext()
 
-const data: IData = inject('data')!
+const data: Settings = inject('data')!
+const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>
 </script>
 
 <template>
@@ -26,19 +27,37 @@ const data: IData = inject('data')!
         </ASelectOption>
       </ASelect>
     </AFormItem>
-    <AFormItem :label="$gettext('API Base Url')">
+    <AFormItem
+      :label="$gettext('API Base Url')"
+      :validate-status="errors?.openai?.base_url ? 'error' : ''"
+      :help="errors?.openai?.base_url === 'url'
+        ? $gettext('The url is not valid')
+        : ''"
+    >
       <AInput
         v-model:value="data.openai.base_url"
         :placeholder="$gettext('Leave blank for the default: https://api.openai.com/')"
       />
     </AFormItem>
-    <AFormItem :label="$gettext('API Proxy')">
+    <AFormItem
+      :label="$gettext('API Proxy')"
+      :validate-status="errors?.openai?.proxy ? 'error' : ''"
+      :help="errors?.openai?.proxy === 'url'
+        ? $gettext('The url is not valid')
+        : ''"
+    >
       <AInput
         v-model:value="data.openai.proxy"
         placeholder="http://127.0.0.1:1087"
       />
     </AFormItem>
-    <AFormItem :label="$gettext('API Token')">
+    <AFormItem
+      :label="$gettext('API Token')"
+      :validate-status="errors?.openai?.token ? 'error' : ''"
+      :help="errors?.openai?.token === 'alphanumdash'
+        ? $gettext('Token is not valid')
+        : ''"
+    >
       <AInputPassword v-model:value="data.openai.token" />
     </AFormItem>
   </AForm>

+ 8 - 2
app/src/views/preference/Preference.vue

@@ -1,16 +1,17 @@
 <script setup lang="ts">
 import { useGettext } from 'vue3-gettext'
 import { message } from 'ant-design-vue'
+import type { Ref } from 'vue'
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 import settings from '@/api/settings'
 import BasicSettings from '@/views/preference/BasicSettings.vue'
 import OpenAISettings from '@/views/preference/OpenAISettings.vue'
 import NginxSettings from '@/views/preference/NginxSettings.vue'
-import type { IData } from '@/views/preference/typedef'
+import type { Settings } from '@/views/preference/typedef'
 
 const { $gettext } = useGettext()
 
-const data = ref<IData>({
+const data = ref<Settings>({
   server: {
     http_host: '0.0.0.0',
     http_port: '9000',
@@ -50,18 +51,23 @@ settings.get().then(r => {
   data.value = r
 })
 
+const errors = ref({}) as Ref<Record<string, Record<string, string>>>
+
 async function save() {
   // fix type
   data.value.server.http_challenge_port = data.value.server.http_challenge_port.toString()
   settings.save(data.value).then(r => {
     data.value = r
     message.success($gettext('Save successfully'))
+    errors.value = {}
   }).catch(e => {
+    errors.value = e.errors
     message.error(e?.message ?? $gettext('Server error'))
   })
 }
 
 provide('data', data)
+provide('errors', errors)
 
 const router = useRouter()
 const route = useRoute()

+ 1 - 1
app/src/views/preference/typedef.ts

@@ -1,4 +1,4 @@
-export interface IData {
+export interface Settings {
   server: {
     http_host: string
     http_port: string