2
0
Seefs 3 сар өмнө
parent
commit
fcc6172b43

+ 5 - 3
service/passkey/service.go

@@ -80,9 +80,11 @@ func BuildWebAuthn(r *http.Request) (*webauthn.WebAuthn, error) {
 }
 
 func resolveOrigins(r *http.Request, settings *system_setting.PasskeySettings) ([]string, error) {
-	if len(settings.Origins) > 0 {
-		origins := make([]string, 0, len(settings.Origins))
-		for _, origin := range settings.Origins {
+	originsStr := strings.TrimSpace(settings.Origins)
+	if originsStr != "" {
+		originList := strings.Split(originsStr, ",")
+		origins := make([]string, 0, len(originList))
+		for _, origin := range originList {
 			trimmed := strings.TrimSpace(origin)
 			if trimmed == "" {
 				continue

+ 20 - 8
setting/system_setting/passkey.go

@@ -1,25 +1,27 @@
 package system_setting
 
 import (
+	"net/url"
 	"one-api/common"
 	"one-api/setting/config"
+	"strings"
 )
 
 type PasskeySettings struct {
-	Enabled              bool     `json:"enabled"`
-	RPDisplayName        string   `json:"rp_display_name"`
-	RPID                 string   `json:"rp_id"`
-	Origins              []string `json:"origins"`
-	AllowInsecureOrigin  bool     `json:"allow_insecure_origin"`
-	UserVerification     string   `json:"user_verification"`
-	AttachmentPreference string   `json:"attachment_preference"`
+	Enabled              bool   `json:"enabled"`
+	RPDisplayName        string `json:"rp_display_name"`
+	RPID                 string `json:"rp_id"`
+	Origins              string `json:"origins"`
+	AllowInsecureOrigin  bool   `json:"allow_insecure_origin"`
+	UserVerification     string `json:"user_verification"`
+	AttachmentPreference string `json:"attachment_preference"`
 }
 
 var defaultPasskeySettings = PasskeySettings{
 	Enabled:              false,
 	RPDisplayName:        common.SystemName,
 	RPID:                 "",
-	Origins:              []string{},
+	Origins:              "",
 	AllowInsecureOrigin:  false,
 	UserVerification:     "preferred",
 	AttachmentPreference: "",
@@ -30,5 +32,15 @@ func init() {
 }
 
 func GetPasskeySettings() *PasskeySettings {
+	if defaultPasskeySettings.RPID == "" && ServerAddress != "" {
+		// 从ServerAddress提取域名作为RPID
+		// ServerAddress可能是 "https://newapi.pro" 这种格式
+		serverAddr := strings.TrimSpace(ServerAddress)
+		if parsed, err := url.Parse(serverAddr); err == nil && parsed.Host != "" {
+			defaultPasskeySettings.RPID = parsed.Host
+		} else {
+			defaultPasskeySettings.RPID = serverAddr
+		}
+	}
 	return &defaultPasskeySettings
 }

+ 38 - 61
web/src/components/settings/SystemSetting.jsx

@@ -122,7 +122,6 @@ const SystemSetting = () => {
   const [domainList, setDomainList] = useState([]);
   const [ipList, setIpList] = useState([]);
   const [allowedPorts, setAllowedPorts] = useState([]);
-  const [passkeyOrigins, setPasskeyOrigins] = useState([]);
 
   const getOptions = async () => {
     setLoading(true);
@@ -188,22 +187,19 @@ const SystemSetting = () => {
             item.value = toBoolean(item.value);
             break;
           case 'passkey.origins':
-            try {
-              const origins = item.value ? JSON.parse(item.value) : [];
-              setPasskeyOrigins(Array.isArray(origins) ? origins : []);
-              item.value = Array.isArray(origins) ? origins : [];
-            } catch (e) {
-              setPasskeyOrigins([]);
-              item.value = [];
-            }
+            // origins是逗号分隔的字符串,直接使用
+            item.value = item.value || '';
             break;
           case 'passkey.rp_display_name':
           case 'passkey.rp_id':
-          case 'passkey.user_verification':
           case 'passkey.attachment_preference':
             // 确保字符串字段不为null/undefined
             item.value = item.value || '';
             break;
+          case 'passkey.user_verification':
+            // 确保有默认值
+            item.value = item.value || 'preferred';
+            break;
           case 'Price':
           case 'MinTopUp':
             item.value = parseFloat(item.value);
@@ -611,42 +607,33 @@ const SystemSetting = () => {
   };
 
   const submitPasskeySettings = async () => {
+    // 使用formApi直接获取当前表单值
+    const formValues = formApiRef.current?.getValues() || {};
+
     const options = [];
 
-    // 只在值有变化时才提交,并确保空值转换为空字符串
-    if (originInputs['passkey.rp_display_name'] !== inputs['passkey.rp_display_name']) {
-      options.push({
-        key: 'passkey.rp_display_name',
-        value: inputs['passkey.rp_display_name'] || '',
-      });
-    }
-    if (originInputs['passkey.rp_id'] !== inputs['passkey.rp_id']) {
-      options.push({
-        key: 'passkey.rp_id',
-        value: inputs['passkey.rp_id'] || '',
-      });
-    }
-    if (originInputs['passkey.user_verification'] !== inputs['passkey.user_verification']) {
-      options.push({
-        key: 'passkey.user_verification',
-        value: inputs['passkey.user_verification'] || 'preferred',
-      });
-    }
-    if (originInputs['passkey.attachment_preference'] !== inputs['passkey.attachment_preference']) {
-      options.push({
-        key: 'passkey.attachment_preference',
-        value: inputs['passkey.attachment_preference'] || '',
-      });
-    }
-    // Origins总是提交,因为它们可能会被用户清空
+    options.push({
+      key: 'passkey.rp_display_name',
+      value: formValues['passkey.rp_display_name'] || inputs['passkey.rp_display_name'] || '',
+    });
+    options.push({
+      key: 'passkey.rp_id',
+      value: formValues['passkey.rp_id'] || inputs['passkey.rp_id'] || '',
+    });
+    options.push({
+      key: 'passkey.user_verification',
+      value: formValues['passkey.user_verification'] || inputs['passkey.user_verification'] || 'preferred',
+    });
+    options.push({
+      key: 'passkey.attachment_preference',
+      value: formValues['passkey.attachment_preference'] || inputs['passkey.attachment_preference'] || '',
+    });
     options.push({
       key: 'passkey.origins',
-      value: JSON.stringify(Array.isArray(passkeyOrigins) ? passkeyOrigins : []),
+      value: formValues['passkey.origins'] || inputs['passkey.origins'] || '',
     });
 
-    if (options.length > 0) {
-      await updateOptions(options);
-    }
+    await updateOptions(options);
   };
 
   const handleCheckboxChange = async (optionKey, event) => {
@@ -1037,7 +1024,7 @@ const SystemSetting = () => {
                   >
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
                       <Form.Checkbox
-                        field='passkey.enabled'
+                        field="['passkey.enabled']"
                         noLabel
                         onChange={(e) =>
                           handleCheckboxChange('passkey.enabled', e)
@@ -1052,7 +1039,7 @@ const SystemSetting = () => {
                   >
                     <Col xs={24} sm={24} md={12} lg={12} xl={12}>
                       <Form.Input
-                        field='passkey.rp_display_name'
+                        field="['passkey.rp_display_name']"
                         label={t('服务显示名称')}
                         placeholder={t('默认使用系统名称')}
                         extraText={t('用户注册时看到的网站名称,比如\'我的网站\'')}
@@ -1060,7 +1047,7 @@ const SystemSetting = () => {
                     </Col>
                     <Col xs={24} sm={24} md={12} lg={12} xl={12}>
                       <Form.Input
-                        field='passkey.rp_id'
+                        field="['passkey.rp_id']"
                         label={t('网站域名标识')}
                         placeholder={t('例如:example.com')}
                         extraText={t('留空自动使用当前域名')}
@@ -1073,7 +1060,7 @@ const SystemSetting = () => {
                   >
                     <Col xs={24} sm={24} md={12} lg={12} xl={12}>
                       <Form.Select
-                        field='passkey.user_verification'
+                        field="['passkey.user_verification']"
                         label={t('安全验证级别')}
                         placeholder={t('是否要求指纹/面容等生物识别')}
                         optionList={[
@@ -1086,7 +1073,7 @@ const SystemSetting = () => {
                     </Col>
                     <Col xs={24} sm={24} md={12} lg={12} xl={12}>
                       <Form.Select
-                        field='passkey.attachment_preference'
+                        field="['passkey.attachment_preference']"
                         label={t('设备类型偏好')}
                         placeholder={t('选择支持的认证设备类型')}
                         optionList={[
@@ -1104,7 +1091,7 @@ const SystemSetting = () => {
                   >
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
                       <Form.Checkbox
-                        field='passkey.allow_insecure_origin'
+                        field="['passkey.allow_insecure_origin']"
                         noLabel
                         extraText={t('仅用于开发环境,生产环境应使用 HTTPS')}
                         onChange={(e) =>
@@ -1120,21 +1107,11 @@ const SystemSetting = () => {
                     style={{ marginTop: 16 }}
                   >
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
-                      <Text strong>{t('允许的 Origins')}</Text>
-                      <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
-                        {t('留空将自动使用服务器地址,多个 Origin 用于支持多域名部署')}
-                      </Text>
-                      <TagInput
-                        value={passkeyOrigins}
-                        onChange={(value) => {
-                          setPasskeyOrigins(value);
-                          setInputs(prev => ({
-                            ...prev,
-                            'passkey.origins': value
-                          }));
-                        }}
-                        placeholder={t('输入 Origin 后回车,如:https://example.com')}
-                        style={{ width: '100%' }}
+                      <Form.Input
+                        field="['passkey.origins']"
+                        label={t('允许的 Origins')}
+                        placeholder={t('填写带https的域名,逗号分隔')}
+                        extraText={t('空的话则不限制 Origin,多个 Origin 用逗号分隔')}
                       />
                     </Col>
                   </Row>