mirror of
https://github.com/linux-do/new-api.git
synced 2025-11-09 15:43:41 +08:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # README.md # go.mod # go.sum # model/log.go # web/src/App.js # web/src/components/ChannelsTable.js
This commit is contained in:
545
web/src/App.js
545
web/src/App.js
@@ -1,20 +1,20 @@
|
||||
import React, {lazy, Suspense, useContext, useEffect} from 'react';
|
||||
import {Route, Routes} from 'react-router-dom';
|
||||
import React, { lazy, Suspense, useContext, useEffect } from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import Loading from './components/Loading';
|
||||
import User from './pages/User';
|
||||
import {PrivateRoute} from './components/PrivateRoute';
|
||||
import { PrivateRoute } from './components/PrivateRoute';
|
||||
import RegisterForm from './components/RegisterForm';
|
||||
import LoginForm from './components/LoginForm';
|
||||
import NotFound from './pages/NotFound';
|
||||
import Setting from './pages/Setting';
|
||||
import EditUser from './pages/User/EditUser';
|
||||
import AddUser from './pages/User/AddUser';
|
||||
import {API, getLogo, getSystemName, showError, showNotice} from './helpers';
|
||||
import { API, getLogo, getSystemName, showError, showNotice } from './helpers';
|
||||
import PasswordResetForm from './components/PasswordResetForm';
|
||||
import GitHubOAuth from './components/GitHubOAuth';
|
||||
import PasswordResetConfirm from './components/PasswordResetConfirm';
|
||||
import {UserContext} from './context/User';
|
||||
import {StatusContext} from './context/Status';
|
||||
import { UserContext } from './context/User';
|
||||
import { StatusContext } from './context/Status';
|
||||
import Channel from './pages/Channel';
|
||||
import Token from './pages/Token';
|
||||
import EditToken from './pages/Token/EditToken';
|
||||
@@ -24,295 +24,270 @@ import EditRedemption from './pages/Redemption/EditRedemption';
|
||||
import TopUp from './pages/TopUp';
|
||||
import Log from './pages/Log';
|
||||
import Chat from './pages/Chat';
|
||||
import Midjourney from './pages/Midjourney';
|
||||
|
||||
const Home = lazy(() => import('./pages/Home'));
|
||||
const About = lazy(() => import('./pages/About'));
|
||||
|
||||
function App() {
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
||||
|
||||
const loadUser = () => {
|
||||
let user = localStorage.getItem('user');
|
||||
if (user) {
|
||||
let data = JSON.parse(user);
|
||||
userDispatch({type: 'login', payload: data});
|
||||
}
|
||||
};
|
||||
const loadStatus = async () => {
|
||||
const res = await API.get('/api/status');
|
||||
const {success, data} = res.data;
|
||||
if (success) {
|
||||
localStorage.setItem('status', JSON.stringify(data));
|
||||
statusDispatch({type: 'set', payload: data});
|
||||
localStorage.setItem('system_name', data.system_name);
|
||||
localStorage.setItem('logo', data.logo);
|
||||
localStorage.setItem('footer_html', data.footer_html);
|
||||
localStorage.setItem('quota_per_unit', data.quota_per_unit);
|
||||
localStorage.setItem('display_in_currency', data.display_in_currency);
|
||||
if (data.chat_link) {
|
||||
localStorage.setItem('chat_link', data.chat_link);
|
||||
} else {
|
||||
localStorage.removeItem('chat_link');
|
||||
}
|
||||
if (
|
||||
data.version !== process.env.REACT_APP_VERSION &&
|
||||
data.version !== 'v0.0.0' &&
|
||||
process.env.REACT_APP_VERSION !== ''
|
||||
) {
|
||||
showNotice(
|
||||
`新版本可用:${data.version},请使用快捷键 Shift + F5 刷新页面`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showError('无法正常连接至服务器!');
|
||||
}
|
||||
};
|
||||
const loadUser = () => {
|
||||
let user = localStorage.getItem('user');
|
||||
if (user) {
|
||||
let data = JSON.parse(user);
|
||||
userDispatch({ type: 'login', payload: data });
|
||||
}
|
||||
};
|
||||
const loadStatus = async () => {
|
||||
const res = await API.get('/api/status');
|
||||
const { success, data } = res.data;
|
||||
if (success) {
|
||||
localStorage.setItem('status', JSON.stringify(data));
|
||||
statusDispatch({ type: 'set', payload: data });
|
||||
localStorage.setItem('system_name', data.system_name);
|
||||
localStorage.setItem('logo', data.logo);
|
||||
localStorage.setItem('footer_html', data.footer_html);
|
||||
localStorage.setItem('quota_per_unit', data.quota_per_unit);
|
||||
localStorage.setItem('display_in_currency', data.display_in_currency);
|
||||
if (data.chat_link) {
|
||||
localStorage.setItem('chat_link', data.chat_link);
|
||||
} else {
|
||||
localStorage.removeItem('chat_link');
|
||||
}
|
||||
if (
|
||||
data.version !== process.env.REACT_APP_VERSION &&
|
||||
data.version !== 'v0.0.0' &&
|
||||
process.env.REACT_APP_VERSION !== ''
|
||||
) {
|
||||
showNotice(
|
||||
`新版本可用:${data.version},请使用快捷键 Shift + F5 刷新页面`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showError('无法正常连接至服务器!');
|
||||
}
|
||||
};
|
||||
|
||||
// const getOptions = async () => {
|
||||
// const res = await API.get('/api/option/');
|
||||
// const {success, message, data} = res.data;
|
||||
// if (success) {
|
||||
// let newInputs = {};
|
||||
// data.forEach((item) => {
|
||||
// if (item.key === 'ModelRatio' || item.key === 'GroupRatio') {
|
||||
// item.value = JSON.stringify(JSON.parse(item.value), null, 2);
|
||||
// }
|
||||
// newInputs[item.key] = item.value;
|
||||
// });
|
||||
// setInputs(newInputs);
|
||||
// setOriginInputs(newInputs);
|
||||
// } else {
|
||||
// showError(message);
|
||||
// }
|
||||
// };
|
||||
useEffect(() => {
|
||||
loadUser();
|
||||
loadStatus().then();
|
||||
let systemName = getSystemName();
|
||||
if (systemName) {
|
||||
document.title = systemName;
|
||||
}
|
||||
let logo = getLogo();
|
||||
if (logo) {
|
||||
let linkElement = document.querySelector("link[rel~='icon']");
|
||||
if (linkElement) {
|
||||
linkElement.href = logo;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadUser();
|
||||
loadStatus().then();
|
||||
let systemName = getSystemName();
|
||||
if (systemName) {
|
||||
document.title = systemName;
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path='/'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Home />
|
||||
</Suspense>
|
||||
}
|
||||
let logo = getLogo();
|
||||
if (logo) {
|
||||
let linkElement = document.querySelector("link[rel~='icon']");
|
||||
if (linkElement) {
|
||||
linkElement.href = logo;
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/channel'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Channel />
|
||||
</PrivateRoute>
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path='/'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Home/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/channel'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Channel/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/channel/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditChannel/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/channel/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditChannel/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Token/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditToken/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditToken/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Redemption/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditRedemption/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditRedemption/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<User/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditUser/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/edit'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditUser/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<AddUser/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/reset'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<PasswordResetConfirm/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/login'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<LoginForm/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/register'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<RegisterForm/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/reset'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<PasswordResetForm/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/oauth/github'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<GitHubOAuth/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/setting'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Setting/>
|
||||
</Suspense>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/topup'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<TopUp/>
|
||||
</Suspense>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/log'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Log/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/midjourney'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Midjourney/>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/about'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<About/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/chat'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Chat/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path='*' element={NotFound}/>
|
||||
</Routes>
|
||||
);
|
||||
/>
|
||||
<Route
|
||||
path='/channel/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditChannel />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/channel/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditChannel />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Token />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditToken />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/token/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditToken />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Redemption />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditRedemption />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/redemption/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditRedemption />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<User />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/edit/:id'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditUser />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/edit'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<EditUser />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/add'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<AddUser />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/user/reset'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<PasswordResetConfirm />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/login'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/register'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<RegisterForm />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/reset'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<PasswordResetForm />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/oauth/github'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<GitHubOAuth />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/setting'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Setting />
|
||||
</Suspense>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/topup'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<TopUp />
|
||||
</Suspense>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/log'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Log />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/about'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<About />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/chat'
|
||||
element={
|
||||
<Suspense fallback={<Loading></Loading>}>
|
||||
<Chat />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path='*' element={
|
||||
<NotFound />
|
||||
} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {Button, Form, Input, Label, Pagination, Popup, Table} from 'semantic-ui-react';
|
||||
import { Button, Form, Input, Label, Message, Pagination, Popup, Table } from 'semantic-ui-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { API, showError, showInfo, showNotice, showSuccess, timestamp2string } from '../helpers';
|
||||
import { API, setPromptShown, shouldShowPrompt, showError, showInfo, showSuccess, timestamp2string } from '../helpers';
|
||||
|
||||
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
|
||||
import {renderGroup, renderNumber, renderQuota} from '../helpers/render';
|
||||
@@ -56,6 +56,7 @@ const ChannelsTable = () => {
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [updatingBalance, setUpdatingBalance] = useState(false);
|
||||
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
||||
const [showPrompt, setShowPrompt] = useState(shouldShowPrompt("channel-test"));
|
||||
|
||||
const loadChannels = async (startIdx) => {
|
||||
const res = await API.get(`/api/channel/?p=${startIdx}&page_size=${pageSize}`);
|
||||
@@ -104,7 +105,7 @@ const ChannelsTable = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const manageChannel = async (id, action, idx, priority) => {
|
||||
const manageChannel = async (id, action, idx, value) => {
|
||||
let data = { id };
|
||||
let res;
|
||||
switch (action) {
|
||||
@@ -120,10 +121,20 @@ const ChannelsTable = () => {
|
||||
res = await API.put('/api/channel/', data);
|
||||
break;
|
||||
case 'priority':
|
||||
if (priority === '') {
|
||||
if (value === '') {
|
||||
return;
|
||||
}
|
||||
data.priority = parseInt(priority);
|
||||
data.priority = parseInt(value);
|
||||
res = await API.put('/api/channel/', data);
|
||||
break;
|
||||
case 'weight':
|
||||
if (value === '') {
|
||||
return;
|
||||
}
|
||||
data.weight = parseInt(value);
|
||||
if (data.weight < 0) {
|
||||
data.weight = 0;
|
||||
}
|
||||
res = await API.put('/api/channel/', data);
|
||||
break;
|
||||
}
|
||||
@@ -150,9 +161,23 @@ const ChannelsTable = () => {
|
||||
return <Label basic color='green'>已启用</Label>;
|
||||
case 2:
|
||||
return (
|
||||
<Label basic color='red'>
|
||||
已禁用
|
||||
</Label>
|
||||
<Popup
|
||||
trigger={<Label basic color='red'>
|
||||
已禁用
|
||||
</Label>}
|
||||
content='本渠道被手动禁用'
|
||||
basic
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<Popup
|
||||
trigger={<Label basic color='yellow'>
|
||||
已禁用
|
||||
</Label>}
|
||||
content='本渠道被程序自动禁用'
|
||||
basic
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
@@ -210,7 +235,6 @@ const ChannelsTable = () => {
|
||||
showInfo(`通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
||||
} else {
|
||||
showError(message);
|
||||
showNotice("当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo 模型进行非流式请求实现的,因此测试报错并不一定代表通道不可用,该功能后续会修复。")
|
||||
}
|
||||
};
|
||||
|
||||
@@ -224,6 +248,17 @@ const ChannelsTable = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAllDisabledChannels = async () => {
|
||||
const res = await API.delete(`/api/channel/disabled`);
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
showSuccess(`已删除所有禁用渠道,共计 ${data} 个`);
|
||||
await refresh();
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const updateChannelBalance = async (id, name, idx) => {
|
||||
const res = await API.get(`/api/channel/update_balance/${id}/`);
|
||||
const { success, message, balance } = res.data;
|
||||
@@ -290,7 +325,19 @@ const ChannelsTable = () => {
|
||||
onChange={handleKeywordChange}
|
||||
/>
|
||||
</Form>
|
||||
{
|
||||
showPrompt && (
|
||||
<Message onDismiss={() => {
|
||||
setShowPrompt(false);
|
||||
setPromptShown("channel-test");
|
||||
}}>
|
||||
当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo
|
||||
模型进行非流式请求实现的,因此测试报错并不一定代表通道不可用,该功能后续会修复。
|
||||
|
||||
另外,OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 0。对于支持的渠道类型,请点击余额进行刷新。
|
||||
</Message>
|
||||
)
|
||||
}
|
||||
<Table basic compact size='small'>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
@@ -363,10 +410,10 @@ const ChannelsTable = () => {
|
||||
余额
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
sortChannel('priority');
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
sortChannel('priority');
|
||||
}}
|
||||
>
|
||||
优先级
|
||||
</Table.HeaderCell>
|
||||
@@ -411,18 +458,18 @@ const ChannelsTable = () => {
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Popup
|
||||
trigger={<Input type="number" defaultValue={channel.priority} onBlur={(event) => {
|
||||
manageChannel(
|
||||
channel.id,
|
||||
'priority',
|
||||
idx,
|
||||
event.target.value,
|
||||
);
|
||||
}}>
|
||||
<input style={{maxWidth:'60px'}} />
|
||||
</Input>}
|
||||
content='渠道选择优先级,越高越优先'
|
||||
basic
|
||||
trigger={<Input type='number' defaultValue={channel.priority} onBlur={(event) => {
|
||||
manageChannel(
|
||||
channel.id,
|
||||
'priority',
|
||||
idx,
|
||||
event.target.value
|
||||
);
|
||||
}}>
|
||||
<input style={{ maxWidth: '60px' }} />
|
||||
</Input>}
|
||||
content='渠道选择优先级,越高越优先'
|
||||
basic
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
@@ -519,6 +566,31 @@ const ChannelsTable = () => {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Popup
|
||||
trigger={
|
||||
<Button size='small' loading={loading}>
|
||||
删除禁用渠道
|
||||
</Button>
|
||||
}
|
||||
on='click'
|
||||
flowing
|
||||
hoverable
|
||||
>
|
||||
<Button size='small' loading={loading} negative onClick={deleteAllDisabledChannels}>
|
||||
确认删除
|
||||
</Button>
|
||||
</Popup>
|
||||
<Pagination
|
||||
floated='right'
|
||||
activePage={activePage}
|
||||
onPageChange={onPaginationChange}
|
||||
size='small'
|
||||
siblingRange={1}
|
||||
totalPages={
|
||||
Math.ceil(channels.length / ITEMS_PER_PAGE) +
|
||||
(channels.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
||||
}
|
||||
/>
|
||||
<Button size='small' onClick={refresh} loading={loading}>刷新</Button>
|
||||
|
||||
</Table.HeaderCell>
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Button, Divider, Form, Grid, Header, Image, Message, Modal, Segment } from 'semantic-ui-react';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { UserContext } from '../context/User';
|
||||
import { API, getLogo, showError, showSuccess } from '../helpers';
|
||||
import { getOAuthState, onGitHubOAuthClicked } from './utils';
|
||||
import { API, getLogo, showError, showSuccess, showWarning } from '../helpers';
|
||||
import { onGitHubOAuthClicked } from './utils';
|
||||
|
||||
const LoginForm = () => {
|
||||
const [inputs, setInputs] = useState({
|
||||
@@ -68,8 +68,14 @@ const LoginForm = () => {
|
||||
if (success) {
|
||||
userDispatch({ type: 'login', payload: data });
|
||||
localStorage.setItem('user', JSON.stringify(data));
|
||||
navigate('/');
|
||||
showSuccess('登录成功!');
|
||||
if (username === 'root' && password === '123456') {
|
||||
navigate('/user/edit');
|
||||
showSuccess('登录成功!');
|
||||
showWarning('请立刻修改默认密码!');
|
||||
} else {
|
||||
navigate('/token');
|
||||
showSuccess('登录成功!');
|
||||
}
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
@@ -126,7 +132,7 @@ const LoginForm = () => {
|
||||
circular
|
||||
color='black'
|
||||
icon='github'
|
||||
onClick={()=>onGitHubOAuthClicked(status.github_client_id)}
|
||||
onClick={() => onGitHubOAuthClicked(status.github_client_id)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
|
||||
@@ -138,7 +138,7 @@ const TokensTable = () => {
|
||||
let defaultUrl;
|
||||
|
||||
if (chatLink) {
|
||||
defaultUrl = chatLink + `/#/?settings={"key":"sk-${key}"}`;
|
||||
defaultUrl = chatLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
} else {
|
||||
defaultUrl = `https://chat.oneapi.pro/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export const CHANNEL_OPTIONS = [
|
||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||
{ key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet' },
|
||||
{ key: 19, text: '360 智脑', value: 19, color: 'blue' },
|
||||
{ key: 23, text: '腾讯混元', value: 23, color: 'teal' },
|
||||
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
||||
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
||||
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
||||
|
||||
@@ -186,4 +186,14 @@ export const verifyJSON = (str) => {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
export function shouldShowPrompt(id) {
|
||||
let prompt = localStorage.getItem(`prompt-${id}`);
|
||||
return !prompt;
|
||||
|
||||
}
|
||||
|
||||
export function setPromptShown(id) {
|
||||
localStorage.setItem(`prompt-${id}`, 'true');
|
||||
}
|
||||
@@ -19,6 +19,8 @@ function type2secretPrompt(type) {
|
||||
return '按照如下格式输入:APPID|APISecret|APIKey';
|
||||
case 22:
|
||||
return '按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041';
|
||||
case 23:
|
||||
return '按照如下格式输入:AppId|SecretId|SecretKey';
|
||||
default:
|
||||
return '请输入渠道对应的鉴权密钥';
|
||||
}
|
||||
@@ -80,7 +82,10 @@ const EditChannel = () => {
|
||||
localModels = ['SparkDesk'];
|
||||
break;
|
||||
case 19:
|
||||
localModels = ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1', '360GPT_S2_V9.4'];
|
||||
localModels = ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1'];
|
||||
break;
|
||||
case 23:
|
||||
localModels = ['hunyuan'];
|
||||
break;
|
||||
}
|
||||
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Segment, Header } from 'semantic-ui-react';
|
||||
import { Message } from 'semantic-ui-react';
|
||||
|
||||
const NotFound = () => (
|
||||
<>
|
||||
<Header
|
||||
block
|
||||
as="h4"
|
||||
content="404"
|
||||
attached="top"
|
||||
icon="info"
|
||||
className="small-icon"
|
||||
/>
|
||||
<Segment attached="bottom">
|
||||
未找到所请求的页面
|
||||
</Segment>
|
||||
<Message negative>
|
||||
<Message.Header>页面不存在</Message.Header>
|
||||
<p>请检查你的浏览器地址是否正确</p>
|
||||
</Message>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ const EditUser = () => {
|
||||
label='密码'
|
||||
name='password'
|
||||
type={'password'}
|
||||
placeholder={'请输入新的密码'}
|
||||
placeholder={'请输入新的密码,最短 8 位'}
|
||||
onChange={handleInputChange}
|
||||
value={password}
|
||||
autoComplete='new-password'
|
||||
|
||||
Reference in New Issue
Block a user