Просмотр исходного кода

feat: Introduce configurable docs link and remove hardcoded chat links

- Added a new GeneralSetting struct to manage configurable docs link
- Removed hardcoded ChatLink and ChatLink2 variables across multiple files
- Updated frontend components to dynamically render docs link from status
- Simplified chat and link-related logic in various components
- Added a warning modal for quota per unit setting in operation settings
[email protected] 9 месяцев назад
Родитель
Сommit
00c2d6c102

+ 3 - 2
common/constants.go

@@ -15,8 +15,9 @@ var SystemName = "New API"
 var Footer = ""
 var Logo = ""
 var TopUpLink = ""
-var ChatLink = ""
-var ChatLink2 = ""
+
+// var ChatLink = ""
+// var ChatLink2 = ""
 var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
 var DisplayInCurrencyEnabled = true
 var DisplayTokenStatEnabled = true

+ 2 - 0
controller/channel-test.go

@@ -186,6 +186,8 @@ func buildTestRequest(model string) *dto.GeneralOpenAIRequest {
 	// 并非Embedding 模型
 	if strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") {
 		testRequest.MaxCompletionTokens = 10
+	} else if strings.Contains(model, "thinking") {
+		testRequest.MaxTokens = 50
 	} else {
 		testRequest.MaxTokens = 10
 	}

+ 1 - 2
controller/misc.go

@@ -54,8 +54,7 @@ func GetStatus(c *gin.Context) {
 			"turnstile_check":          common.TurnstileCheckEnabled,
 			"turnstile_site_key":       common.TurnstileSiteKey,
 			"top_up_link":              common.TopUpLink,
-			"chat_link":                common.ChatLink,
-			"chat_link2":               common.ChatLink2,
+			"docs_link":                operation_setting.GetGeneralSetting().DocsLink,
 			"quota_per_unit":           common.QuotaPerUnit,
 			"display_in_currency":      common.DisplayInCurrencyEnabled,
 			"enable_batch_update":      common.BatchUpdateEnabled,

+ 6 - 6
model/option.go

@@ -99,8 +99,8 @@ func InitOptionMap() {
 	common.OptionMap["UserUsableGroups"] = setting.UserUsableGroups2JSONString()
 	common.OptionMap["CompletionRatio"] = operation_setting.CompletionRatio2JSONString()
 	common.OptionMap["TopUpLink"] = common.TopUpLink
-	common.OptionMap["ChatLink"] = common.ChatLink
-	common.OptionMap["ChatLink2"] = common.ChatLink2
+	//common.OptionMap["ChatLink"] = common.ChatLink
+	//common.OptionMap["ChatLink2"] = common.ChatLink2
 	common.OptionMap["QuotaPerUnit"] = strconv.FormatFloat(common.QuotaPerUnit, 'f', -1, 64)
 	common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
 	common.OptionMap["DataExportInterval"] = strconv.Itoa(common.DataExportInterval)
@@ -358,10 +358,10 @@ func updateOptionMap(key string, value string) (err error) {
 		err = operation_setting.UpdateCacheRatioByJSONString(value)
 	case "TopUpLink":
 		common.TopUpLink = value
-	case "ChatLink":
-		common.ChatLink = value
-	case "ChatLink2":
-		common.ChatLink2 = value
+	//case "ChatLink":
+	//	common.ChatLink = value
+	//case "ChatLink2":
+	//	common.ChatLink2 = value
 	case "ChannelDisableThreshold":
 		common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64)
 	case "QuotaPerUnit":

+ 21 - 0
setting/operation_setting/general_setting.go

@@ -0,0 +1,21 @@
+package operation_setting
+
+import "one-api/setting/config"
+
+type GeneralSetting struct {
+	DocsLink string `json:"docs_link"`
+}
+
+// 默认配置
+var generalSetting = GeneralSetting{
+	DocsLink: "https://docs.newapi.pro",
+}
+
+func init() {
+	// 注册到全局配置管理器
+	config.GlobalConfig.Register("general_setting", &generalSetting)
+}
+
+func GetGeneralSetting() *GeneralSetting {
+	return &generalSetting
+}

+ 27 - 7
web/src/components/HeaderBar.js

@@ -44,6 +44,7 @@ const HeaderBar = () => {
 
   // Check if self-use mode is enabled
   const isSelfUseMode = statusState?.status?.self_use_mode_enabled || false;
+  const docsLink = statusState?.status?.docs_link || '';
   const isDemoSiteMode = statusState?.status?.demo_site_enabled || false;
 
   let buttons = [
@@ -62,6 +63,13 @@ const HeaderBar = () => {
       itemKey: 'pricing',
       to: '/pricing',
     },
+    // Only include the docs button if docsLink exists
+    ...(docsLink ? [{
+      text: t('文档'),
+      itemKey: 'docs',
+      isExternal: true,
+      externalLink: docsLink,
+    }] : []),
     {
       text: t('关于'),
       itemKey: 'about',
@@ -157,13 +165,25 @@ const HeaderBar = () => {
                     }
                   }
                 }}>
-                  <Link
-                    className="header-bar-text"
-                    style={{ textDecoration: 'none' }}
-                    to={routerMap[props.itemKey]}
-                  >
-                    {itemElement}
-                  </Link>
+                  {props.isExternal ? (
+                    <a
+                      className="header-bar-text"
+                      style={{ textDecoration: 'none' }}
+                      href={props.externalLink}
+                      target="_blank"
+                      rel="noopener noreferrer"
+                    >
+                      {itemElement}
+                    </a>
+                  ) : (
+                    <Link
+                      className="header-bar-text"
+                      style={{ textDecoration: 'none' }}
+                      to={routerMap[props.itemKey]}
+                    >
+                      {itemElement}
+                    </Link>
+                  )}
                 </div>
               );
             }}

+ 2 - 2
web/src/components/OperationSetting.js

@@ -34,8 +34,8 @@ const OperationSetting = () => {
     GroupRatio: '',
     UserUsableGroups: '',
     TopUpLink: '',
-    ChatLink: '',
-    ChatLink2: '', // 添加的新状态变量
+    'general_setting.docs_link': '',
+    // ChatLink2: '', // 添加的新状态变量
     QuotaPerUnit: 0,
     AutomaticDisableChannelEnabled: false,
     AutomaticEnableChannelEnabled: false,

+ 33 - 39
web/src/components/SiderBar.js

@@ -196,31 +196,28 @@ const SiderBar = () => {
     }
     setSelectedKeys([localKey]);
 
-    let chatLink = localStorage.getItem('chat_link');
-    if (!chatLink) {
-      let chats = localStorage.getItem('chats');
-      if (chats) {
-        // console.log(chats);
-        try {
-          chats = JSON.parse(chats);
-          if (Array.isArray(chats)) {
-            let chatItems = [];
-            for (let i = 0; i < chats.length; i++) {
-              let chat = {};
-              for (let key in chats[i]) {
-                chat.text = key;
-                chat.itemKey = 'chat' + i;
-                chat.to = '/chat/' + i;
-              }
-              // setRouterMap({ ...routerMap, chat: '/chat/' + i })
-              chatItems.push(chat);
+    let chats = localStorage.getItem('chats');
+    if (chats) {
+      // console.log(chats);
+      try {
+        chats = JSON.parse(chats);
+        if (Array.isArray(chats)) {
+          let chatItems = [];
+          for (let i = 0; i < chats.length; i++) {
+            let chat = {};
+            for (let key in chats[i]) {
+              chat.text = key;
+              chat.itemKey = 'chat' + i;
+              chat.to = '/chat/' + i;
             }
-            setChatItems(chatItems);
+            // setRouterMap({ ...routerMap, chat: '/chat/' + i })
+            chatItems.push(chat);
           }
-        } catch (e) {
-          console.error(e);
-          showError('聊天数据解析失败')
+          setChatItems(chatItems);
         }
+      } catch (e) {
+        console.error(e);
+        showError('聊天数据解析失败')
       }
     }
 
@@ -254,24 +251,21 @@ const SiderBar = () => {
         }}
         selectedKeys={selectedKeys}
         renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
-          let chatLink = localStorage.getItem('chat_link');
-          if (!chatLink) {
-            let chats = localStorage.getItem('chats');
-            if (chats) {
-              chats = JSON.parse(chats);
-              if (Array.isArray(chats) && chats.length > 0) {
-                for (let i = 0; i < chats.length; i++) {
-                  routerMap['chat' + i] = '/chat/' + i;
-                }
-                if (chats.length > 1) {
-                  // delete /chat
-                  if (routerMap['chat']) {
-                    delete routerMap['chat'];
-                  }
-                } else {
-                  // rename /chat to /chat/0
-                  routerMap['chat'] = '/chat/0';
+          let chats = localStorage.getItem('chats');
+          if (chats) {
+            chats = JSON.parse(chats);
+            if (Array.isArray(chats) && chats.length > 0) {
+              for (let i = 0; i < chats.length; i++) {
+                routerMap['chat' + i] = '/chat/' + i;
+              }
+              if (chats.length > 1) {
+                // delete /chat
+                if (routerMap['chat']) {
+                  delete routerMap['chat'];
                 }
+              } else {
+                // rename /chat to /chat/0
+                routerMap['chat'] = '/chat/0';
               }
             }
           }

+ 1 - 26
web/src/components/TokensTable.js

@@ -144,33 +144,8 @@ const TokensTable = () => {
       render: (text, record, index) => {
         let chats = localStorage.getItem('chats');
         let chatsArray = []
-        let chatLink = localStorage.getItem('chat_link');
-        let mjLink = localStorage.getItem('chat_link2');
         let shouldUseCustom = true;
-        if (chatLink) {
-          shouldUseCustom = false;
-          chatLink += `/#/?settings={"key":"{key}","url":"{address}"}`;
-          chatsArray.push({
-            node: 'item',
-            key: 'default',
-            name: 'ChatGPT Next Web',
-            onClick: () => {
-              onOpenLink('default', chatLink, record);
-            },
-          });
-        }
-        if (mjLink) {
-          shouldUseCustom = false;
-          mjLink += `/#/?settings={"key":"{key}","url":"{address}"}`;
-          chatsArray.push({
-            node: 'item',
-            key: 'mj',
-            name: 'ChatGPT Next Midjourney',
-            onClick: () => {
-              onOpenLink('mj', mjLink, record);
-            },
-          });
-        }
+
         if (shouldUseCustom) {
           try {
             // console.log(chats);

+ 7 - 2
web/src/helpers/data.js

@@ -19,15 +19,20 @@ export function setStatusData(data) {
   );
   localStorage.setItem('mj_notify_enabled', data.mj_notify_enabled);
   if (data.chat_link) {
-    localStorage.setItem('chat_link', data.chat_link);
+    // localStorage.setItem('chat_link', data.chat_link);
   } else {
     localStorage.removeItem('chat_link');
   }
   if (data.chat_link2) {
-    localStorage.setItem('chat_link2', data.chat_link2);
+    // localStorage.setItem('chat_link2', data.chat_link2);
   } else {
     localStorage.removeItem('chat_link2');
   }
+  if (data.docs_link) {
+    localStorage.setItem('docs_link', data.docs_link);
+  } else {
+    localStorage.removeItem('docs_link');
+  }
 }
 
 export function setUserData(data) {

+ 2 - 4
web/src/pages/Chat/index.js

@@ -10,10 +10,8 @@ const ChatPage = () => {
   const comLink = (key) => {
     // console.log('chatLink:', chatLink);
     if (!serverAddress || !key) return '';
-      let link = localStorage.getItem('chat_link');
-      if (link) {
-          link = `${link}/#/?settings={"key":"sk-${key}","url":"${encodeURIComponent(serverAddress)}"}`;
-      } else if (id) {
+      let link = "";
+      if (id) {
           let chats = localStorage.getItem('chats');
           if (chats) {
               chats = JSON.parse(chats);

+ 24 - 16
web/src/pages/Setting/Operation/SettingsGeneral.js

@@ -1,5 +1,5 @@
 import React, { useEffect, useState, useRef } from 'react';
-import { Banner, Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
+import { Banner, Button, Col, Form, Row, Spin, Collapse, Modal } from '@douyinfe/semi-ui';
 import {
   compareObjects,
   API,
@@ -12,10 +12,10 @@ import { useTranslation } from 'react-i18next';
 export default function GeneralSettings(props) {
   const { t } = useTranslation();
   const [loading, setLoading] = useState(false);
+  const [showQuotaWarning, setShowQuotaWarning] = useState(false);
   const [inputs, setInputs] = useState({
     TopUpLink: '',
-    ChatLink: '',
-    ChatLink2: '',
+    'general_setting.docs_link': '',
     QuotaPerUnit: '',
     RetryTimes: '',
     DisplayInCurrencyEnabled: false,
@@ -104,20 +104,10 @@ export default function GeneralSettings(props) {
               </Col>
               <Col span={8}>
                 <Form.Input
-                  field={'ChatLink'}
-                  label={t('默认聊天页面链接')}
+                  field={'general_setting.docs_link'}
+                  label={t('文档地址')}
                   initValue={''}
-                  placeholder={t('例如 ChatGPT Next Web 的部署地址')}
-                  onChange={onChange}
-                  showClear
-                />
-              </Col>
-              <Col span={8}>
-                <Form.Input
-                  field={'ChatLink2'}
-                  label={t('聊天页面 2 链接')}
-                  initValue={''}
-                  placeholder={t('例如 ChatGPT Next Web 的部署地址')}
+                  placeholder={t('例如 https://docs.newapi.pro')}
                   onChange={onChange}
                   showClear
                 />
@@ -130,6 +120,7 @@ export default function GeneralSettings(props) {
                   placeholder={t('一单位货币能兑换的额度')}
                   onChange={onChange}
                   showClear
+                  onClick={() => setShowQuotaWarning(true)}
                 />
               </Col>
               <Col span={8}>
@@ -231,6 +222,23 @@ export default function GeneralSettings(props) {
           </Form.Section>
         </Form>
       </Spin>
+      
+      <Modal
+        title={t('警告')}
+        visible={showQuotaWarning}
+        onOk={() => setShowQuotaWarning(false)}
+        onCancel={() => setShowQuotaWarning(false)}
+        closeOnEsc={true}
+        width={500}
+      >
+        <Banner
+          type='warning'
+          description={t('此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。')}
+          bordered
+          fullMode={false}
+          closeIcon={null}
+        />
+      </Modal>
     </>
   );
 }

+ 1 - 4
web/src/pages/Setting/index.js

@@ -16,10 +16,7 @@ const Setting = () => {
   const navigate = useNavigate();
   const location = useLocation();
   const [tabActiveKey, setTabActiveKey] = useState('1');
-  let panes = [
-    {
-    },
-  ];
+  let panes = [];
 
   if (isRoot()) {
     panes.push({