merge upstream

Signed-off-by: wozulong <>
This commit is contained in:
wozulong 2024-04-18 15:18:28 +08:00
commit c74e43b8fd
10 changed files with 191 additions and 68 deletions

View File

@ -7,7 +7,7 @@ all: build-frontend start-backend
build-frontend: build-frontend:
@echo "Building 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: start-backend:
@echo "Starting backend dev server..." @echo "Starting backend dev server..."

View File

@ -18,11 +18,23 @@ type Adaptor struct {
func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) { 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) { func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
version := "v1" // 从映射中获取模型名称对应的版本,如果找不到就使用 info.ApiVersion 或默认的版本 "v1"
version, beta := modelVersionMap[info.UpstreamModelName]
if !beta {
if info.ApiVersion != "" { if info.ApiVersion != "" {
version = info.ApiVersion version = info.ApiVersion
} else {
version = "v1"
} }
}
action := "generateContent" action := "generateContent"
if info.IsStream { if info.IsStream {
action = "streamGenerateContent" action = "streamGenerateContent"

View File

@ -5,8 +5,8 @@ const (
) )
var ModelList = []string{ var ModelList = []string{
"gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro", "gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra",
"gemini-pro-vision", "gemini-1.0-pro-vision-001", "gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
} }
var ChannelName = "google gemini" var ChannelName = "google gemini"

View File

@ -179,7 +179,13 @@ func xunfeiHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, appId s
case stop = <-stopChan: case stop = <-stopChan:
} }
} }
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
{
Content: "",
},
}
}
xunfeiResponse.Payload.Choices.Text[0].Content = content xunfeiResponse.Payload.Choices.Text[0].Content = content
response := responseXunfei2OpenAI(&xunfeiResponse) response := responseXunfei2OpenAI(&xunfeiResponse)

View File

@ -254,6 +254,19 @@ const ChannelsTable = () => {
> >
编辑 编辑
</Button> </Button>
<Popconfirm
title='确定是否要复制此渠道?'
content='复制渠道的所有信息'
okType={'danger'}
position={'left'}
onConfirm={async () => {
copySelectedChannel(record.id);
}}
>
<Button theme='light' type='primary' style={{ marginRight: 1 }}>
复制
</Button>
</Popconfirm>
</div> </div>
), ),
}, },
@ -340,6 +353,33 @@ const ChannelsTable = () => {
setLoading(false); 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 () => { const refresh = async () => {
await loadChannels(activePage - 1, pageSize, idSort); await loadChannels(activePage - 1, pageSize, idSort);
}; };

View File

@ -1,6 +1,7 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { UserContext } from '../context/User'; import { UserContext } from '../context/User';
import { useSetTheme, useTheme } from '../context/Theme';
import { API, getLogo, getSystemName, showSuccess } from '../helpers'; import { API, getLogo, getSystemName, showSuccess } from '../helpers';
import '../index.css'; import '../index.css';
@ -34,10 +35,8 @@ const HeaderBar = () => {
let navigate = useNavigate(); let navigate = useNavigate();
const [showSidebar, setShowSidebar] = useState(false); const [showSidebar, setShowSidebar] = useState(false);
const [dark, setDark] = useState(false);
const systemName = getSystemName(); const systemName = getSystemName();
const logo = getLogo(); const logo = getLogo();
var themeMode = localStorage.getItem('theme-mode');
const currentDate = new Date(); const currentDate = new Date();
// enable fireworks on new year(1.1 and 2.9-2.24) // enable fireworks on new year(1.1 and 2.9-2.24)
const isNewYear = const isNewYear =
@ -66,26 +65,19 @@ const HeaderBar = () => {
}, 3000); }, 3000);
}; };
const theme = useTheme();
const setTheme = useSetTheme();
useEffect(() => { useEffect(() => {
if (themeMode === 'dark') { if (theme === 'dark') {
switchMode(true); document.body.setAttribute('theme-mode', 'dark');
} }
if (isNewYear) { if (isNewYear) {
console.log('Happy New Year!'); 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 ( return (
<> <>
<Layout> <Layout>
@ -132,9 +124,11 @@ const HeaderBar = () => {
<Switch <Switch
checkedText='🌞' checkedText='🌞'
size={'large'} size={'large'}
checked={dark} checked={theme === 'dark'}
uncheckedText='🌙' uncheckedText='🌙'
onChange={switchMode} onChange={(checked) => {
setTheme(checked);
}}
/> />
{userState.user ? ( {userState.user ? (
<> <>

View File

@ -8,6 +8,8 @@ import {
verifyJSON, verifyJSON,
} from '../helpers'; } from '../helpers';
import { useTheme } from '../context/Theme';
const OperationSetting = () => { const OperationSetting = () => {
let now = new Date(); let now = new Date();
let [inputs, setInputs] = useState({ let [inputs, setInputs] = useState({
@ -77,6 +79,9 @@ const OperationSetting = () => {
} }
}; };
const theme = useTheme();
const isDark = theme === 'dark';
useEffect(() => { useEffect(() => {
getOptions().then(); getOptions().then();
}, []); }, []);
@ -219,8 +224,10 @@ const OperationSetting = () => {
return ( return (
<Grid columns={1}> <Grid columns={1}>
<Grid.Column> <Grid.Column>
<Form loading={loading}> <Form loading={loading} inverted={isDark}>
<Header as='h3'>通用设置</Header> <Header as='h3' inverted={isDark}>
通用设置
</Header>
<Form.Group widths={4}> <Form.Group widths={4}>
<Form.Input <Form.Input
label='充值链接' label='充值链接'
@ -299,7 +306,9 @@ const OperationSetting = () => {
保存通用设置 保存通用设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>绘图设置</Header> <Header as='h3' inverted={isDark}>
绘图设置
</Header>
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.DrawingEnabled === 'true'} checked={inputs.DrawingEnabled === 'true'}
@ -321,7 +330,9 @@ const OperationSetting = () => {
/> />
</Form.Group> </Form.Group>
<Divider /> <Divider />
<Header as='h3'>屏蔽词过滤设置</Header> <Header as='h3' inverted={isDark}>
屏蔽词过滤设置
</Header>
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.CheckSensitiveEnabled === 'true'} checked={inputs.CheckSensitiveEnabled === 'true'}
@ -381,7 +392,9 @@ const OperationSetting = () => {
保存屏蔽词设置 保存屏蔽词设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>日志设置</Header> <Header as='h3' inverted={isDark}>
日志设置
</Header>
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.LogConsumeEnabled === 'true'} checked={inputs.LogConsumeEnabled === 'true'}
@ -409,7 +422,9 @@ const OperationSetting = () => {
清理历史日志 清理历史日志
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>数据看板</Header> <Header as='h3' inverted={isDark}>
数据看板
</Header>
<Form.Checkbox <Form.Checkbox
checked={inputs.DataExportEnabled === 'true'} checked={inputs.DataExportEnabled === 'true'}
label='启用数据看板(实验性)' label='启用数据看板(实验性)'
@ -439,7 +454,9 @@ const OperationSetting = () => {
/> />
</Form.Group> </Form.Group>
<Divider /> <Divider />
<Header as='h3'>监控设置</Header> <Header as='h3' inverted={isDark}>
监控设置
</Header>
<Form.Group widths={3}> <Form.Group widths={3}>
<Form.Input <Form.Input
label='最长响应时间' label='最长响应时间'
@ -484,7 +501,9 @@ const OperationSetting = () => {
保存监控设置 保存监控设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>额度设置</Header> <Header as='h3' inverted={isDark}>
额度设置
</Header>
<Form.Group widths={4}> <Form.Group widths={4}>
<Form.Input <Form.Input
label='新用户初始额度' label='新用户初始额度'
@ -535,7 +554,9 @@ const OperationSetting = () => {
保存额度设置 保存额度设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>倍率设置</Header> <Header as='h3' inverted={isDark}>
倍率设置
</Header>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.TextArea <Form.TextArea
label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)' label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'

View File

@ -10,6 +10,8 @@ import {
} from 'semantic-ui-react'; } from 'semantic-ui-react';
import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers'; import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
import { useTheme } from '../context/Theme';
const SystemSetting = () => { const SystemSetting = () => {
let [inputs, setInputs] = useState({ let [inputs, setInputs] = useState({
PasswordLoginEnabled: '', PasswordLoginEnabled: '',
@ -62,6 +64,9 @@ const SystemSetting = () => {
const [showPasswordWarningModal, setShowPasswordWarningModal] = const [showPasswordWarningModal, setShowPasswordWarningModal] =
useState(false); useState(false);
const theme = useTheme();
const isDark = theme === 'dark';
const getOptions = async () => { const getOptions = async () => {
const res = await API.get('/api/option/'); const res = await API.get('/api/option/');
const { success, message, data } = res.data; const { success, message, data } = res.data;
@ -346,8 +351,10 @@ const SystemSetting = () => {
return ( return (
<Grid columns={1}> <Grid columns={1}>
<Grid.Column> <Grid.Column>
<Form loading={loading}> <Form loading={loading} inverted={isDark}>
<Header as='h3'>通用设置</Header> <Header as='h3' inverted={isDark}>
通用设置
</Header>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.Input <Form.Input
label='服务器地址' label='服务器地址'
@ -361,7 +368,7 @@ const SystemSetting = () => {
更新服务器地址 更新服务器地址
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
支付设置当前仅支持Stripe Checkout 支付设置当前仅支持Stripe Checkout
<Header.Subheader> <Header.Subheader>
密钥Webhook 等设置请 密钥Webhook 等设置请
@ -455,7 +462,9 @@ const SystemSetting = () => {
/> />
</Form.Group> </Form.Group>
<Divider /> <Divider />
<Header as='h3'>配置登录注册</Header> <Header as='h3' inverted={isDark}>
配置登录注册
</Header>
<Form.Group inline> <Form.Group inline>
<Form.Checkbox <Form.Checkbox
checked={inputs.PasswordLoginEnabled === 'true'} checked={inputs.PasswordLoginEnabled === 'true'}
@ -550,7 +559,7 @@ const SystemSetting = () => {
/> />
</Form.Group> </Form.Group>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
配置邮箱域名白名单 配置邮箱域名白名单
<Header.Subheader> <Header.Subheader>
用以防止恶意用户利用临时邮箱批量注册 用以防止恶意用户利用临时邮箱批量注册
@ -615,7 +624,7 @@ const SystemSetting = () => {
保存邮箱域名白名单设置 保存邮箱域名白名单设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
配置 SMTP 配置 SMTP
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader> <Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
</Header> </Header>
@ -674,7 +683,7 @@ const SystemSetting = () => {
</Form.Group> </Form.Group>
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button> <Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
配置 GitHub OAuth App 配置 GitHub OAuth App
<Header.Subheader> <Header.Subheader>
用以支持通过 GitHub 进行登录注册 用以支持通过 GitHub 进行登录注册
@ -768,7 +777,7 @@ const SystemSetting = () => {
保存 LINUX DO OAuth 设置 保存 LINUX DO OAuth 设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
配置 WeChat Server 配置 WeChat Server
<Header.Subheader> <Header.Subheader>
用以支持通过微信进行登录注册 用以支持通过微信进行登录注册
@ -813,7 +822,9 @@ const SystemSetting = () => {
保存 WeChat Server 设置 保存 WeChat Server 设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'>配置 Telegram 登录</Header> <Header as='h3' inverted={isDark}>
配置 Telegram 登录
</Header>
<Form.Group inline> <Form.Group inline>
<Form.Input <Form.Input
label='Telegram Bot Token' label='Telegram Bot Token'
@ -834,7 +845,7 @@ const SystemSetting = () => {
保存 Telegram 登录设置 保存 Telegram 登录设置
</Form.Button> </Form.Button>
<Divider /> <Divider />
<Header as='h3'> <Header as='h3' inverted={isDark}>
配置 Turnstile 配置 Turnstile
<Header.Subheader> <Header.Subheader>
用以支持用户校验 用以支持用户校验

View File

@ -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 (
<SetThemeContext.Provider value={setTheme}>
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
</SetThemeContext.Provider>
);
};

View File

@ -12,6 +12,7 @@ import 'react-toastify/dist/ReactToastify.css';
import { StatusProvider } from './context/Status'; import { StatusProvider } from './context/Status';
import { Layout } from '@douyinfe/semi-ui'; import { Layout } from '@douyinfe/semi-ui';
import SiderBar from './components/SiderBar'; import SiderBar from './components/SiderBar';
import { ThemeProvider } from './context/Theme';
// initialization // initialization
@ -22,6 +23,7 @@ root.render(
<StatusProvider> <StatusProvider>
<UserProvider> <UserProvider>
<BrowserRouter> <BrowserRouter>
<ThemeProvider>
<Layout> <Layout>
<Sider> <Sider>
<SiderBar /> <SiderBar />
@ -43,6 +45,7 @@ root.render(
</Layout> </Layout>
<ToastContainer /> <ToastContainer />
</Layout> </Layout>
</ThemeProvider>
</BrowserRouter> </BrowserRouter>
</UserProvider> </UserProvider>
</StatusProvider> </StatusProvider>