diff --git a/Makefile b/Makefile
index 35b78d5..51e569a 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ all: build-frontend start-backend
build-frontend:
@echo "Building frontend..."
- @cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) yarn build
+ @cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat VERSION) yarn build
start-backend:
@echo "Starting backend dev server..."
diff --git a/relay/channel/gemini/adaptor.go b/relay/channel/gemini/adaptor.go
index a275175..daaadc5 100644
--- a/relay/channel/gemini/adaptor.go
+++ b/relay/channel/gemini/adaptor.go
@@ -18,16 +18,28 @@ type Adaptor struct {
func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) {
}
+// 定义一个映射,存储模型名称和对应的版本
+var modelVersionMap = map[string]string{
+ "gemini-1.5-pro-latest": "v1beta",
+ "gemini-ultra": "v1beta",
+}
+
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
- version := "v1"
- if info.ApiVersion != "" {
- version = info.ApiVersion
- }
- action := "generateContent"
- if info.IsStream {
- action = "streamGenerateContent"
- }
- return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
+ // 从映射中获取模型名称对应的版本,如果找不到就使用 info.ApiVersion 或默认的版本 "v1"
+ version, beta := modelVersionMap[info.UpstreamModelName]
+ if !beta {
+ if info.ApiVersion != "" {
+ version = info.ApiVersion
+ } else {
+ version = "v1"
+ }
+ }
+
+ action := "generateContent"
+ if info.IsStream {
+ action = "streamGenerateContent"
+ }
+ return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
}
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error {
diff --git a/relay/channel/gemini/constant.go b/relay/channel/gemini/constant.go
index 582f4c0..5e833bc 100644
--- a/relay/channel/gemini/constant.go
+++ b/relay/channel/gemini/constant.go
@@ -5,8 +5,8 @@ const (
)
var ModelList = []string{
- "gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
- "gemini-pro-vision", "gemini-1.0-pro-vision-001",
+ "gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra",
+ "gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
}
var ChannelName = "google gemini"
diff --git a/relay/channel/xunfei/relay-xunfei.go b/relay/channel/xunfei/relay-xunfei.go
index da86002..1690e96 100644
--- a/relay/channel/xunfei/relay-xunfei.go
+++ b/relay/channel/xunfei/relay-xunfei.go
@@ -179,7 +179,13 @@ func xunfeiHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, appId s
case stop = <-stopChan:
}
}
-
+ if len(xunfeiResponse.Payload.Choices.Text) == 0 {
+ xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
+ {
+ Content: "",
+ },
+ }
+ }
xunfeiResponse.Payload.Choices.Text[0].Content = content
response := responseXunfei2OpenAI(&xunfeiResponse)
diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js
index 0d2ed57..3e3930c 100644
--- a/web/src/components/ChannelsTable.js
+++ b/web/src/components/ChannelsTable.js
@@ -254,6 +254,19 @@ const ChannelsTable = () => {
>
编辑
+ {
+ copySelectedChannel(record.id);
+ }}
+ >
+
+
),
},
@@ -340,6 +353,33 @@ const ChannelsTable = () => {
setLoading(false);
};
+ const copySelectedChannel = async (id) => {
+ const channelToCopy = channels.find(
+ (channel) => String(channel.id) === String(id),
+ );
+ console.log(channelToCopy);
+ channelToCopy.name += '_复制';
+ channelToCopy.created_time = null;
+ channelToCopy.balance = 0;
+ channelToCopy.used_quota = 0;
+ if (!channelToCopy) {
+ showError('渠道未找到,请刷新页面后重试。');
+ return;
+ }
+ try {
+ const newChannel = { ...channelToCopy, id: undefined };
+ const response = await API.post('/api/channel/', newChannel);
+ if (response.data.success) {
+ showSuccess('渠道复制成功');
+ await refresh();
+ } else {
+ showError(response.data.message);
+ }
+ } catch (error) {
+ showError('渠道复制失败: ' + error.message);
+ }
+ };
+
const refresh = async () => {
await loadChannels(activePage - 1, pageSize, idSort);
};
diff --git a/web/src/components/HeaderBar.js b/web/src/components/HeaderBar.js
index a5da4df..5510d42 100644
--- a/web/src/components/HeaderBar.js
+++ b/web/src/components/HeaderBar.js
@@ -1,6 +1,7 @@
import React, { useContext, useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { UserContext } from '../context/User';
+import { useSetTheme, useTheme } from '../context/Theme';
import { API, getLogo, getSystemName, showSuccess } from '../helpers';
import '../index.css';
@@ -34,10 +35,8 @@ const HeaderBar = () => {
let navigate = useNavigate();
const [showSidebar, setShowSidebar] = useState(false);
- const [dark, setDark] = useState(false);
const systemName = getSystemName();
const logo = getLogo();
- var themeMode = localStorage.getItem('theme-mode');
const currentDate = new Date();
// enable fireworks on new year(1.1 and 2.9-2.24)
const isNewYear =
@@ -66,26 +65,19 @@ const HeaderBar = () => {
}, 3000);
};
+ const theme = useTheme();
+ const setTheme = useSetTheme();
+
useEffect(() => {
- if (themeMode === 'dark') {
- switchMode(true);
+ if (theme === 'dark') {
+ document.body.setAttribute('theme-mode', 'dark');
}
+
if (isNewYear) {
console.log('Happy New Year!');
}
}, []);
- const switchMode = (model) => {
- const body = document.body;
- if (!model) {
- body.removeAttribute('theme-mode');
- localStorage.setItem('theme-mode', 'light');
- } else {
- body.setAttribute('theme-mode', 'dark');
- localStorage.setItem('theme-mode', 'dark');
- }
- setDark(model);
- };
return (
<>
@@ -132,9 +124,11 @@ const HeaderBar = () => {
{
+ setTheme(checked);
+ }}
/>
{userState.user ? (
<>
diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js
index 14b70d7..7a112e5 100644
--- a/web/src/components/OperationSetting.js
+++ b/web/src/components/OperationSetting.js
@@ -8,6 +8,8 @@ import {
verifyJSON,
} from '../helpers';
+import { useTheme } from '../context/Theme';
+
const OperationSetting = () => {
let now = new Date();
let [inputs, setInputs] = useState({
@@ -77,6 +79,9 @@ const OperationSetting = () => {
}
};
+ const theme = useTheme();
+ const isDark = theme === 'dark';
+
useEffect(() => {
getOptions().then();
}, []);
@@ -219,8 +224,10 @@ const OperationSetting = () => {
return (
-
{
保存通用设置
-
+
{
/>
-
+
{
保存屏蔽词设置
-
+
{
清理历史日志
-
+
{
/>
-
+
{
保存监控设置
-
+
{
保存额度设置
-
+
{
let [inputs, setInputs] = useState({
PasswordLoginEnabled: '',
@@ -62,6 +64,9 @@ const SystemSetting = () => {
const [showPasswordWarningModal, setShowPasswordWarningModal] =
useState(false);
+ const theme = useTheme();
+ const isDark = theme === 'dark';
+
const getOptions = async () => {
const res = await API.get('/api/option/');
const { success, message, data } = res.data;
@@ -346,8 +351,10 @@ const SystemSetting = () => {
return (
-
{
更新服务器地址
-
+
支付设置(当前仅支持Stripe Checkout)
密钥、Webhook 等设置请
@@ -455,7 +462,9 @@ const SystemSetting = () => {
/>
-
+
{
/>
-
+
配置邮箱域名白名单
用以防止恶意用户利用临时邮箱批量注册
@@ -615,7 +624,7 @@ const SystemSetting = () => {
保存邮箱域名白名单设置
-
+
@@ -674,7 +683,7 @@ const SystemSetting = () => {
保存 SMTP 设置
-
+
配置 GitHub OAuth App
用以支持通过 GitHub 进行登录注册,
@@ -768,7 +777,7 @@ const SystemSetting = () => {
保存 LINUX DO OAuth 设置
-
+
配置 WeChat Server
用以支持通过微信进行登录注册,
@@ -813,7 +822,9 @@ const SystemSetting = () => {
保存 WeChat Server 设置
-
+
{
保存 Telegram 登录设置
-
+
配置 Turnstile
用以支持用户校验,
diff --git a/web/src/context/Theme/index.js b/web/src/context/Theme/index.js
new file mode 100644
index 0000000..7654988
--- /dev/null
+++ b/web/src/context/Theme/index.js
@@ -0,0 +1,36 @@
+import { createContext, useCallback, useContext, useState } from 'react';
+
+const ThemeContext = createContext(null);
+export const useTheme = () => useContext(ThemeContext);
+
+const SetThemeContext = createContext(null);
+export const useSetTheme = () => useContext(SetThemeContext);
+
+export const ThemeProvider = ({ children }) => {
+ const [theme, _setTheme] = useState(() => {
+ try {
+ return localStorage.getItem('theme-mode') || null;
+ } catch {
+ return null;
+ }
+ });
+
+ const setTheme = useCallback((input) => {
+ _setTheme(input ? 'dark' : 'light');
+
+ const body = document.body;
+ if (!input) {
+ body.removeAttribute('theme-mode');
+ localStorage.setItem('theme-mode', 'light');
+ } else {
+ body.setAttribute('theme-mode', 'dark');
+ localStorage.setItem('theme-mode', 'dark');
+ }
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/web/src/index.js b/web/src/index.js
index c73daef..94b2286 100644
--- a/web/src/index.js
+++ b/web/src/index.js
@@ -12,6 +12,7 @@ import 'react-toastify/dist/ReactToastify.css';
import { StatusProvider } from './context/Status';
import { Layout } from '@douyinfe/semi-ui';
import SiderBar from './components/SiderBar';
+import { ThemeProvider } from './context/Theme';
// initialization
@@ -22,27 +23,29 @@ root.render(
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+