From 00c2d6c102f7664176b25f86469bb39b8e33db75 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sun, 9 Mar 2025 18:31:16 +0800 Subject: [PATCH] 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 --- common/constants.go | 5 +- controller/channel-test.go | 2 + controller/misc.go | 3 +- model/option.go | 12 ++-- setting/operation_setting/general_setting.go | 21 ++++++ web/src/components/HeaderBar.js | 34 +++++++-- web/src/components/OperationSetting.js | 4 +- web/src/components/SiderBar.js | 72 +++++++++---------- web/src/components/TokensTable.js | 27 +------ web/src/helpers/data.js | 9 ++- web/src/pages/Chat/index.js | 6 +- .../Setting/Operation/SettingsGeneral.js | 40 ++++++----- web/src/pages/Setting/index.js | 5 +- 13 files changed, 130 insertions(+), 110 deletions(-) create mode 100644 setting/operation_setting/general_setting.go diff --git a/common/constants.go b/common/constants.go index bcab24fc..9611ed0e 100644 --- a/common/constants.go +++ b/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 diff --git a/controller/channel-test.go b/controller/channel-test.go index 6a1ffe43..46fb6e9f 100644 --- a/controller/channel-test.go +++ b/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 } diff --git a/controller/misc.go b/controller/misc.go index a451b5e3..0c2ed5e7 100644 --- a/controller/misc.go +++ b/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, diff --git a/model/option.go b/model/option.go index fe12eab1..d575742f 100644 --- a/model/option.go +++ b/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": diff --git a/setting/operation_setting/general_setting.go b/setting/operation_setting/general_setting.go new file mode 100644 index 00000000..787f0e5f --- /dev/null +++ b/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 +} diff --git a/web/src/components/HeaderBar.js b/web/src/components/HeaderBar.js index 68169ed2..74c6add1 100644 --- a/web/src/components/HeaderBar.js +++ b/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 = () => { } } }}> - - {itemElement} - + {props.isExternal ? ( + + {itemElement} + + ) : ( + + {itemElement} + + )} ); }} diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js index 85d883d5..2e650a5e 100644 --- a/web/src/components/OperationSetting.js +++ b/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, diff --git a/web/src/components/SiderBar.js b/web/src/components/SiderBar.js index 92ef9bf4..f2a27c8f 100644 --- a/web/src/components/SiderBar.js +++ b/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'; } } } diff --git a/web/src/components/TokensTable.js b/web/src/components/TokensTable.js index 2d82df35..ba2a8be3 100644 --- a/web/src/components/TokensTable.js +++ b/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); diff --git a/web/src/helpers/data.js b/web/src/helpers/data.js index 4cd7a641..bc1d28aa 100644 --- a/web/src/helpers/data.js +++ b/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) { diff --git a/web/src/pages/Chat/index.js b/web/src/pages/Chat/index.js index f4639ff1..3c1b1db8 100644 --- a/web/src/pages/Chat/index.js +++ b/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); diff --git a/web/src/pages/Setting/Operation/SettingsGeneral.js b/web/src/pages/Setting/Operation/SettingsGeneral.js index e46e7db2..bf7b4205 100644 --- a/web/src/pages/Setting/Operation/SettingsGeneral.js +++ b/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) {