mirror of
https://github.com/linux-do/new-api.git
synced 2025-11-08 23:23:42 +08:00
Merge branch 'main' into telegram-login
This commit is contained in:
@@ -29,7 +29,7 @@ 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 [statusState, statusDispatch] = useContext(StatusContext);
|
||||
|
||||
const loadUser = () => {
|
||||
let user = localStorage.getItem('user');
|
||||
@@ -38,47 +38,9 @@ function App() {
|
||||
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);
|
||||
localStorage.setItem('enable_drawing', data.enable_drawing);
|
||||
localStorage.setItem('enable_data_export', data.enable_data_export);
|
||||
localStorage.setItem('data_export_default_time', data.data_export_default_time);
|
||||
if (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);
|
||||
} else {
|
||||
localStorage.removeItem('chat_link2');
|
||||
}
|
||||
// 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('无法正常连接至服务器!');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadUser();
|
||||
loadStatus().then();
|
||||
let systemName = getSystemName();
|
||||
if (systemName) {
|
||||
document.title = systemName;
|
||||
|
||||
@@ -257,6 +257,7 @@ const ChannelsTable = () => {
|
||||
const [idSort, setIdSort] = useState(false);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [searchGroup, setSearchGroup] = useState('');
|
||||
const [searchModel, setSearchModel] = useState('');
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [updatingBalance, setUpdatingBalance] = useState(false);
|
||||
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
||||
@@ -440,15 +441,15 @@ const ChannelsTable = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const searchChannels = async (searchKeyword, searchGroup) => {
|
||||
if (searchKeyword === '' && searchGroup === '') {
|
||||
const searchChannels = async (searchKeyword, searchGroup, searchModel) => {
|
||||
if (searchKeyword === '' && searchGroup === '' && searchModel === '') {
|
||||
// if keyword is blank, load files instead.
|
||||
await loadChannels(0, pageSize, idSort);
|
||||
setActivePage(1);
|
||||
return;
|
||||
}
|
||||
setSearching(true);
|
||||
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}`);
|
||||
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`);
|
||||
const {success, message, data} = res.data;
|
||||
if (success) {
|
||||
setChannels(data);
|
||||
@@ -625,13 +626,12 @@ const ChannelsTable = () => {
|
||||
return (
|
||||
<>
|
||||
<EditChannel refresh={refresh} visible={showEdit} handleClose={closeEdit} editingChannel={editingChannel}/>
|
||||
<Form onSubmit={() => {searchChannels(searchKeyword, searchGroup)}} labelPosition='left'>
|
||||
|
||||
<Form onSubmit={() => {searchChannels(searchKeyword, searchGroup, searchModel)}} labelPosition='left'>
|
||||
<div style={{display: 'flex'}}>
|
||||
<Space>
|
||||
<Form.Input
|
||||
field='search'
|
||||
label='关键词'
|
||||
field='search_keyword'
|
||||
label='搜索渠道关键词'
|
||||
placeholder='ID,名称和密钥 ...'
|
||||
value={searchKeyword}
|
||||
loading={searching}
|
||||
@@ -639,10 +639,22 @@ const ChannelsTable = () => {
|
||||
setSearchKeyword(v.trim())
|
||||
}}
|
||||
/>
|
||||
<Form.Input
|
||||
field='search_model'
|
||||
label='模型'
|
||||
placeholder='模型关键字'
|
||||
value={searchModel}
|
||||
loading={searching}
|
||||
onChange={(v)=>{
|
||||
setSearchModel(v.trim())
|
||||
}}
|
||||
/>
|
||||
<Form.Select field="group" label='分组' optionList={groupOptions} onChange={(v) => {
|
||||
setSearchGroup(v)
|
||||
searchChannels(searchKeyword, v)
|
||||
searchChannels(searchKeyword, v, searchModel)
|
||||
}}/>
|
||||
<Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"
|
||||
style={{marginRight: 8}}>查询</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -27,6 +27,7 @@ const OperationSetting = () => {
|
||||
DataExportEnabled: '',
|
||||
DataExportDefaultTime: 'hour',
|
||||
DataExportInterval: 5,
|
||||
DefaultCollapseSidebar: '', // 默认折叠侧边栏
|
||||
RetryTimes: 0
|
||||
});
|
||||
const [originInputs, setOriginInputs] = useState({});
|
||||
@@ -65,6 +66,10 @@ const OperationSetting = () => {
|
||||
if (key.endsWith('Enabled')) {
|
||||
value = inputs[key] === 'true' ? 'false' : 'true';
|
||||
}
|
||||
if (key === 'DefaultCollapseSidebar') {
|
||||
value = inputs[key] === 'true' ? 'false' : 'true';
|
||||
}
|
||||
console.log(key, value)
|
||||
const res = await API.put('/api/option/', {
|
||||
key,
|
||||
value
|
||||
@@ -79,7 +84,7 @@ const OperationSetting = () => {
|
||||
};
|
||||
|
||||
const handleInputChange = async (e, {name, value}) => {
|
||||
if (name.endsWith('Enabled') || name === 'DataExportInterval' || name === 'DataExportDefaultTime') {
|
||||
if (name.endsWith('Enabled') || name === 'DataExportInterval' || name === 'DataExportDefaultTime' || name === 'DefaultCollapseSidebar') {
|
||||
if (name === 'DataExportDefaultTime') {
|
||||
localStorage.setItem('data_export_default_time', value);
|
||||
}
|
||||
@@ -243,6 +248,12 @@ const OperationSetting = () => {
|
||||
name='DrawingEnabled'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Form.Checkbox
|
||||
checked={inputs.DefaultCollapseSidebar === 'true'}
|
||||
label='默认折叠侧边栏'
|
||||
name='DefaultCollapseSidebar'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Button onClick={() => {
|
||||
submitConfig('general').then();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, {useContext, useMemo, useState} from 'react';
|
||||
import React, { useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import {Link, useNavigate} from 'react-router-dom';
|
||||
import {UserContext} from '../context/User';
|
||||
|
||||
import {API, getLogo, getSystemName, isAdmin, isMobile, showSuccess} from '../helpers';
|
||||
import { API, getLogo, getSystemName, isAdmin, isMobile, showError, showSuccess } from '../helpers';
|
||||
import '../index.css';
|
||||
|
||||
import {
|
||||
@@ -24,11 +24,14 @@ import {Nav, Avatar, Dropdown, Layout} from '@douyinfe/semi-ui';
|
||||
|
||||
const SiderBar = () => {
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
const defaultIsCollapsed = isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true';
|
||||
|
||||
let navigate = useNavigate();
|
||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||
const [showSidebar, setShowSidebar] = useState(false);
|
||||
const systemName = getSystemName();
|
||||
const logo = getLogo();
|
||||
const [isCollapsed, setIsCollapsed] = useState(defaultIsCollapsed);
|
||||
|
||||
const headerButtons = useMemo(() => [
|
||||
{
|
||||
text: '首页',
|
||||
@@ -110,15 +113,41 @@ const SiderBar = () => {
|
||||
// }
|
||||
], [localStorage.getItem('enable_data_export'), localStorage.getItem('enable_drawing'), localStorage.getItem('chat_link'), isAdmin()]);
|
||||
|
||||
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);
|
||||
localStorage.setItem('enable_drawing', data.enable_drawing);
|
||||
localStorage.setItem('enable_data_export', data.enable_data_export);
|
||||
localStorage.setItem('data_export_default_time', data.data_export_default_time);
|
||||
localStorage.setItem('default_collapse_sidebar', data.default_collapse_sidebar);
|
||||
if (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);
|
||||
} else {
|
||||
localStorage.removeItem('chat_link2');
|
||||
}
|
||||
} else {
|
||||
showError('无法正常连接至服务器!');
|
||||
}
|
||||
};
|
||||
|
||||
async function logout() {
|
||||
setShowSidebar(false);
|
||||
await API.get('/api/user/logout');
|
||||
showSuccess('注销成功!');
|
||||
userDispatch({type: 'logout'});
|
||||
localStorage.removeItem('user');
|
||||
navigate('/login');
|
||||
}
|
||||
useEffect(() => {
|
||||
loadStatus().then(() => {
|
||||
setIsCollapsed(isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true');
|
||||
});
|
||||
},[])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -127,7 +156,12 @@ const SiderBar = () => {
|
||||
<Nav
|
||||
// mode={'horizontal'}
|
||||
// bodyStyle={{ height: 100 }}
|
||||
defaultIsCollapsed={isMobile()}
|
||||
defaultIsCollapsed={isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true'}
|
||||
isCollapsed={isCollapsed}
|
||||
onCollapseChange={collapsed => {
|
||||
console.log(collapsed);
|
||||
setIsCollapsed(collapsed);
|
||||
}}
|
||||
selectedKeys={selectedKeys}
|
||||
renderWrapper={({itemElement, isSubNav, isInSubNav, props}) => {
|
||||
const routerMap = {
|
||||
|
||||
@@ -20,8 +20,10 @@ const SystemSetting = () => {
|
||||
EpayId: '',
|
||||
EpayKey: '',
|
||||
Price: 7.3,
|
||||
MinTopUp: 1,
|
||||
TopupGroupRatio: '',
|
||||
PayAddress: '',
|
||||
CustomCallbackAddress: '',
|
||||
Footer: '',
|
||||
WeChatAuthEnabled: '',
|
||||
WeChatServerAddress: '',
|
||||
@@ -292,8 +294,8 @@ const SystemSetting = () => {
|
||||
<Form.Button onClick={submitServerAddress}>
|
||||
更新服务器地址
|
||||
</Form.Button>
|
||||
<Divider />
|
||||
<Header as='h3'>支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)</Header>
|
||||
<Divider/>
|
||||
<Header as='h3'>支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)</Header>
|
||||
<Form.Group widths='equal'>
|
||||
<Form.Input
|
||||
label='支付地址,不填写则不启用在线支付'
|
||||
@@ -316,14 +318,31 @@ const SystemSetting = () => {
|
||||
name='EpayKey'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Form.Input
|
||||
label='充值价格(x元/美金)'
|
||||
placeholder='例如:7,就是7元/美金'
|
||||
value={inputs.Price}
|
||||
name='Price'
|
||||
|
||||
min={0}
|
||||
onChange={handleInputChange}
|
||||
</Form.Group>
|
||||
<Form.Group widths='equal'>
|
||||
<Form.Input
|
||||
label='回调地址,不填写则使用上方服务器地址作为回调地址'
|
||||
placeholder='例如:https://yourdomain.com'
|
||||
value={inputs.CustomCallbackAddress}
|
||||
name='CustomCallbackAddress'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Form.Input
|
||||
label='充值价格(x元/美金)'
|
||||
placeholder='例如:7,就是7元/美金'
|
||||
value={inputs.Price}
|
||||
name='Price'
|
||||
min={0}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Form.Input
|
||||
label='最低充值数量'
|
||||
placeholder='例如:2,就是最低充值2$'
|
||||
value={inputs.MinTopUp}
|
||||
name='MinTopUp'
|
||||
min={1}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group widths='equal'>
|
||||
|
||||
@@ -153,7 +153,7 @@ const TokensTable = () => {
|
||||
[
|
||||
{node: 'item', key: 'next', disabled: !localStorage.getItem('chat_link'), name: 'ChatGPT Next Web', onClick: () => {onOpenLink('next', record.key)}},
|
||||
{node: 'item', key: 'next-mj', disabled: !localStorage.getItem('chat_link2'), name: 'ChatGPT Web & Midjourney', onClick: () => {onOpenLink('next-mj', record.key)}},
|
||||
{node: 'item', key: 'ama', name: 'AMA 问天(BotGrem)', onClick: () => {onOpenLink('ama', record.key)}},
|
||||
{node: 'item', key: 'ama', name: 'AMA 问天(BotGem)', onClick: () => {onOpenLink('ama', record.key)}},
|
||||
{node: 'item', key: 'opencat', name: 'OpenCat', onClick: () => {onOpenLink('opencat', record.key)}},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -132,6 +132,8 @@ export const modelColorMap = {
|
||||
'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色
|
||||
'gpt-4-0613': 'rgb(100,149,237)', // 矢车菊蓝
|
||||
'gpt-4-1106-preview': 'rgb(30,144,255)', // 道奇蓝
|
||||
'gpt-4-0125-preview': 'rgb(2,177,236)', // 深天蓝
|
||||
'gpt-4-turbo-preview': 'rgb(2,177,255)', // 深天蓝
|
||||
'gpt-4-32k': 'rgb(104,111,238)', // 中紫色
|
||||
'gpt-4-32k-0314': 'rgb(90,105,205)', // 暗灰蓝色
|
||||
'gpt-4-32k-0613': 'rgb(61,71,139)', // 暗蓝灰色
|
||||
|
||||
@@ -10,6 +10,7 @@ const TopUp = () => {
|
||||
const [topUpCount, setTopUpCount] = useState(10);
|
||||
const [minTopupCount, setMinTopUpCount] = useState(1);
|
||||
const [amount, setAmount] = useState(0.0);
|
||||
const [minTopUp, setMinTopUp] = useState(1);
|
||||
const [topUpLink, setTopUpLink] = useState('');
|
||||
const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false);
|
||||
const [userQuota, setUserQuota] = useState(0);
|
||||
@@ -61,6 +62,10 @@ const TopUp = () => {
|
||||
if (amount === 0) {
|
||||
await getAmount();
|
||||
}
|
||||
if (topUpCount < minTopUp) {
|
||||
showInfo('充值数量不能小于' + minTopUp);
|
||||
return;
|
||||
}
|
||||
setPayWay(payment)
|
||||
setOpen(true);
|
||||
}
|
||||
@@ -69,6 +74,10 @@ const TopUp = () => {
|
||||
if (amount === 0) {
|
||||
await getAmount();
|
||||
}
|
||||
if (topUpCount < minTopUp) {
|
||||
showInfo('充值数量不能小于' + minTopUp);
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
try {
|
||||
const res = await API.post('/api/user/pay', {
|
||||
@@ -132,6 +141,9 @@ const TopUp = () => {
|
||||
if (status.top_up_link) {
|
||||
setTopUpLink(status.top_up_link);
|
||||
}
|
||||
if (status.min_topup) {
|
||||
setMinTopUp(status.min_topup);
|
||||
}
|
||||
if (status.enable_online_topup) {
|
||||
setEnableOnlineTopUp(status.enable_online_topup);
|
||||
}
|
||||
@@ -239,12 +251,13 @@ const TopUp = () => {
|
||||
disabled={!enableOnlineTopUp}
|
||||
field={'redemptionCount'}
|
||||
label={'实付金额:' + renderAmount()}
|
||||
placeholder='充值数量'
|
||||
placeholder={'充值数量,最低' + minTopUp + '$'}
|
||||
name='redemptionCount'
|
||||
type={'number'}
|
||||
value={topUpCount}
|
||||
suffix={'$'}
|
||||
min={1}
|
||||
min={minTopUp}
|
||||
defaultValue={minTopUp}
|
||||
max={100000}
|
||||
onChange={async (value) => {
|
||||
if (value < 1) {
|
||||
|
||||
Reference in New Issue
Block a user