mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-01 06:13:43 +08:00
Compare commits
5 Commits
v0.6.11-al
...
v0.6.11-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a316ed7abc | ||
|
|
0895d8660e | ||
|
|
be1ed114f4 | ||
|
|
eb6da573a3 | ||
|
|
0a6273fc08 |
19
Dockerfile
19
Dockerfile
@@ -4,21 +4,20 @@ WORKDIR /web
|
|||||||
COPY ./VERSION .
|
COPY ./VERSION .
|
||||||
COPY ./web .
|
COPY ./web .
|
||||||
|
|
||||||
WORKDIR /web/default
|
RUN npm install --prefix /web/default & \
|
||||||
RUN npm install
|
npm install --prefix /web/berry & \
|
||||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build
|
npm install --prefix /web/air & \
|
||||||
|
wait
|
||||||
|
|
||||||
WORKDIR /web/berry
|
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/default/VERSION) npm run build --prefix /web/default & \
|
||||||
RUN npm install
|
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/berry/VERSION) npm run build --prefix /web/berry & \
|
||||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build
|
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/air/VERSION) npm run build --prefix /web/air & \
|
||||||
|
wait
|
||||||
WORKDIR /web/air
|
|
||||||
RUN npm install
|
|
||||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build
|
|
||||||
|
|
||||||
FROM golang:alpine AS builder2
|
FROM golang:alpine AS builder2
|
||||||
|
|
||||||
RUN apk add --no-cache g++
|
RUN apk add --no-cache g++
|
||||||
|
RUN apk add --no-cache gcc musl-dev libc-dev sqlite-dev
|
||||||
|
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=1 \
|
CGO_ENABLED=1 \
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Card } from 'semantic-ui-react';
|
import { Card, Header, Segment } from 'semantic-ui-react';
|
||||||
import { API, showError } from '../../helpers';
|
import { API, showError } from '../../helpers';
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
|
|
||||||
@@ -7,39 +7,58 @@ const About = () => {
|
|||||||
const [about, setAbout] = useState('');
|
const [about, setAbout] = useState('');
|
||||||
const [aboutLoaded, setAboutLoaded] = useState(false);
|
const [aboutLoaded, setAboutLoaded] = useState(false);
|
||||||
|
|
||||||
// ... 其他函数保持不变 ...
|
const displayAbout = async () => {
|
||||||
|
setAbout(localStorage.getItem('about') || '');
|
||||||
|
const res = await API.get('/api/about');
|
||||||
|
const { success, message, data } = res.data;
|
||||||
|
if (success) {
|
||||||
|
let aboutContent = data;
|
||||||
|
if (!data.startsWith('https://')) {
|
||||||
|
aboutContent = marked.parse(data);
|
||||||
|
}
|
||||||
|
setAbout(aboutContent);
|
||||||
|
localStorage.setItem('about', aboutContent);
|
||||||
|
} else {
|
||||||
|
showError(message);
|
||||||
|
setAbout('加载关于内容失败...');
|
||||||
|
}
|
||||||
|
setAboutLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
displayAbout().then();
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className='dashboard-container'>
|
<>
|
||||||
<Card fluid className='chart-card'>
|
{aboutLoaded && about === '' ? (
|
||||||
<Card.Content>
|
<div className='dashboard-container'>
|
||||||
<Card.Header className='header'>关于系统</Card.Header>
|
<Card fluid className='chart-card'>
|
||||||
{aboutLoaded && about === '' ? (
|
<Card.Content>
|
||||||
<>
|
<Card.Header className='header'>关于系统</Card.Header>
|
||||||
<p>可在设置页面设置关于内容,支持 HTML & Markdown</p>
|
<p>可在设置页面设置关于内容,支持 HTML & Markdown</p>
|
||||||
项目仓库地址:
|
项目仓库地址:
|
||||||
<a href='https://github.com/songquanpeng/one-api'>
|
<a href='https://github.com/songquanpeng/one-api'>
|
||||||
https://github.com/songquanpeng/one-api
|
https://github.com/songquanpeng/one-api
|
||||||
</a>
|
</a>
|
||||||
</>
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{about.startsWith('https://') ? (
|
||||||
|
<iframe
|
||||||
|
src={about}
|
||||||
|
style={{ width: '100%', height: '100vh', border: 'none' }}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<div
|
||||||
{about.startsWith('https://') ? (
|
style={{ fontSize: 'larger' }}
|
||||||
<iframe
|
dangerouslySetInnerHTML={{ __html: about }}
|
||||||
src={about}
|
></div>
|
||||||
style={{ width: '100%', height: '100vh', border: 'none' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={{ fontSize: 'larger' }}
|
|
||||||
dangerouslySetInnerHTML={{ __html: about }}
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Card.Content>
|
</>
|
||||||
</Card>
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,16 +68,27 @@ const Dashboard = () => {
|
|||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/user/dashboard');
|
const response = await axios.get('/api/user/dashboard');
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
const dashboardData = response.data.data;
|
const dashboardData = response.data.data || [];
|
||||||
setData(dashboardData);
|
setData(dashboardData);
|
||||||
calculateSummary(dashboardData);
|
calculateSummary(dashboardData);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch dashboard data:', error);
|
console.error('Failed to fetch dashboard data:', error);
|
||||||
|
setData([]);
|
||||||
|
calculateSummary([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateSummary = (dashboardData) => {
|
const calculateSummary = (dashboardData) => {
|
||||||
|
if (!Array.isArray(dashboardData) || dashboardData.length === 0) {
|
||||||
|
setSummaryData({
|
||||||
|
todayRequests: 0,
|
||||||
|
todayQuota: 0,
|
||||||
|
todayTokens: 0
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
const todayData = dashboardData.filter((item) => item.Day === today);
|
const todayData = dashboardData.filter((item) => item.Day === today);
|
||||||
|
|
||||||
@@ -87,7 +98,7 @@ const Dashboard = () => {
|
|||||||
0
|
0
|
||||||
),
|
),
|
||||||
todayQuota:
|
todayQuota:
|
||||||
todayData.reduce((sum, item) => sum + item.Quota, 0) / 1000000, // 转换为美元
|
todayData.reduce((sum, item) => sum + item.Quota, 0) / 1000000,
|
||||||
todayTokens: todayData.reduce(
|
todayTokens: todayData.reduce(
|
||||||
(sum, item) => sum + item.PromptTokens + item.CompletionTokens,
|
(sum, item) => sum + item.PromptTokens + item.CompletionTokens,
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -56,218 +56,222 @@ const Home = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='dashboard-container'>
|
<>
|
||||||
<Card fluid className='chart-card'>
|
|
||||||
<Card.Content>
|
|
||||||
<Card.Header className='header'>欢迎使用 One API</Card.Header>
|
|
||||||
<Card.Description style={{ lineHeight: '1.6' }}>
|
|
||||||
<p>
|
|
||||||
One API 是一个 LLM API
|
|
||||||
接口管理和分发系统,可以帮助您更好地管理和使用各大厂商的 LLM API。
|
|
||||||
</p>
|
|
||||||
{!userState.user && (
|
|
||||||
<p>
|
|
||||||
如需使用,请先<Link to='/login'>登录</Link>或
|
|
||||||
<Link to='/register'>注册</Link>。
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</Card.Description>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{homePageContentLoaded && homePageContent === '' ? (
|
{homePageContentLoaded && homePageContent === '' ? (
|
||||||
<Card fluid className='chart-card'>
|
<div className='dashboard-container'>
|
||||||
<Card.Content>
|
<Card fluid className='chart-card'>
|
||||||
<Card.Header>
|
<Card.Content>
|
||||||
<Header as='h3'>系统状况</Header>
|
<Card.Header className='header'>欢迎使用 One API</Card.Header>
|
||||||
</Card.Header>
|
<Card.Description style={{ lineHeight: '1.6' }}>
|
||||||
<Grid columns={2} stackable>
|
<p>
|
||||||
<Grid.Column>
|
One API 是一个 LLM API
|
||||||
<Card
|
接口管理和分发系统,可以帮助您更好地管理和使用各大厂商的 LLM
|
||||||
fluid
|
API。
|
||||||
className='chart-card'
|
</p>
|
||||||
style={{ boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }}
|
{!userState.user && (
|
||||||
>
|
<p>
|
||||||
<Card.Content>
|
如需使用,请先<Link to='/login'>登录</Link>或
|
||||||
<Card.Header>
|
<Link to='/register'>注册</Link>。
|
||||||
<Header as='h3' style={{ color: '#444' }}>
|
</p>
|
||||||
系统信息
|
)}
|
||||||
</Header>
|
</Card.Description>
|
||||||
</Card.Header>
|
</Card.Content>
|
||||||
<Card.Description
|
</Card>
|
||||||
style={{ lineHeight: '2', marginTop: '1em' }}
|
<Card fluid className='chart-card'>
|
||||||
>
|
<Card.Content>
|
||||||
<p
|
<Card.Header>
|
||||||
style={{
|
<Header as='h3'>系统状况</Header>
|
||||||
display: 'flex',
|
</Card.Header>
|
||||||
alignItems: 'center',
|
<Grid columns={2} stackable>
|
||||||
gap: '0.5em',
|
<Grid.Column>
|
||||||
}}
|
<Card
|
||||||
|
fluid
|
||||||
|
className='chart-card'
|
||||||
|
style={{ boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }}
|
||||||
|
>
|
||||||
|
<Card.Content>
|
||||||
|
<Card.Header>
|
||||||
|
<Header as='h3' style={{ color: '#444' }}>
|
||||||
|
系统信息
|
||||||
|
</Header>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Description
|
||||||
|
style={{ lineHeight: '2', marginTop: '1em' }}
|
||||||
>
|
>
|
||||||
<i className='info circle icon'></i>
|
<p
|
||||||
<span style={{ fontWeight: 'bold' }}>名称:</span>
|
style={{
|
||||||
<span>{statusState?.status?.system_name}</span>
|
display: 'flex',
|
||||||
</p>
|
alignItems: 'center',
|
||||||
<p
|
gap: '0.5em',
|
||||||
style={{
|
}}
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '0.5em',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className='code branch icon'></i>
|
|
||||||
<span style={{ fontWeight: 'bold' }}>版本:</span>
|
|
||||||
<span>{statusState?.status?.version || 'unknown'}</span>
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '0.5em',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className='github icon'></i>
|
|
||||||
<span style={{ fontWeight: 'bold' }}>源码:</span>
|
|
||||||
<a
|
|
||||||
href='https://github.com/songquanpeng/one-api'
|
|
||||||
target='_blank'
|
|
||||||
style={{ color: '#2185d0' }}
|
|
||||||
>
|
>
|
||||||
GitHub 仓库
|
<i className='info circle icon'></i>
|
||||||
</a>
|
<span style={{ fontWeight: 'bold' }}>名称:</span>
|
||||||
</p>
|
<span>{statusState?.status?.system_name}</span>
|
||||||
<p
|
</p>
|
||||||
style={{
|
<p
|
||||||
display: 'flex',
|
style={{
|
||||||
alignItems: 'center',
|
display: 'flex',
|
||||||
gap: '0.5em',
|
alignItems: 'center',
|
||||||
}}
|
gap: '0.5em',
|
||||||
>
|
}}
|
||||||
<i className='clock outline icon'></i>
|
>
|
||||||
<span style={{ fontWeight: 'bold' }}>启动时间:</span>
|
<i className='code branch icon'></i>
|
||||||
<span>{getStartTimeString()}</span>
|
<span style={{ fontWeight: 'bold' }}>版本:</span>
|
||||||
</p>
|
<span>
|
||||||
</Card.Description>
|
{statusState?.status?.version || 'unknown'}
|
||||||
</Card.Content>
|
</span>
|
||||||
</Card>
|
</p>
|
||||||
</Grid.Column>
|
<p
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.5em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className='github icon'></i>
|
||||||
|
<span style={{ fontWeight: 'bold' }}>源码:</span>
|
||||||
|
<a
|
||||||
|
href='https://github.com/songquanpeng/one-api'
|
||||||
|
target='_blank'
|
||||||
|
style={{ color: '#2185d0' }}
|
||||||
|
>
|
||||||
|
GitHub 仓库
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.5em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className='clock outline icon'></i>
|
||||||
|
<span style={{ fontWeight: 'bold' }}>启动时间:</span>
|
||||||
|
<span>{getStartTimeString()}</span>
|
||||||
|
</p>
|
||||||
|
</Card.Description>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</Grid.Column>
|
||||||
|
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<Card
|
<Card
|
||||||
fluid
|
fluid
|
||||||
className='chart-card'
|
className='chart-card'
|
||||||
style={{ boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }}
|
style={{ boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }}
|
||||||
>
|
>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
<Header as='h3' style={{ color: '#444' }}>
|
<Header as='h3' style={{ color: '#444' }}>
|
||||||
系统配置
|
系统配置
|
||||||
</Header>
|
</Header>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Description
|
<Card.Description
|
||||||
style={{ lineHeight: '2', marginTop: '1em' }}
|
style={{ lineHeight: '2', marginTop: '1em' }}
|
||||||
>
|
|
||||||
<p
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '0.5em',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<i className='envelope icon'></i>
|
<p
|
||||||
<span style={{ fontWeight: 'bold' }}>邮箱验证:</span>
|
|
||||||
<span
|
|
||||||
style={{
|
style={{
|
||||||
color: statusState?.status?.email_verification
|
display: 'flex',
|
||||||
? '#21ba45'
|
alignItems: 'center',
|
||||||
: '#db2828',
|
gap: '0.5em',
|
||||||
fontWeight: '500',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{statusState?.status?.email_verification
|
<i className='envelope icon'></i>
|
||||||
? '已启用'
|
<span style={{ fontWeight: 'bold' }}>邮箱验证:</span>
|
||||||
: '未启用'}
|
<span
|
||||||
</span>
|
style={{
|
||||||
</p>
|
color: statusState?.status?.email_verification
|
||||||
<p
|
? '#21ba45'
|
||||||
style={{
|
: '#db2828',
|
||||||
display: 'flex',
|
fontWeight: '500',
|
||||||
alignItems: 'center',
|
}}
|
||||||
gap: '0.5em',
|
>
|
||||||
}}
|
{statusState?.status?.email_verification
|
||||||
>
|
? '已启用'
|
||||||
<i className='github icon'></i>
|
: '未启用'}
|
||||||
<span style={{ fontWeight: 'bold' }}>
|
</span>
|
||||||
GitHub 身份验证:
|
</p>
|
||||||
</span>
|
<p
|
||||||
<span
|
|
||||||
style={{
|
style={{
|
||||||
color: statusState?.status?.github_oauth
|
display: 'flex',
|
||||||
? '#21ba45'
|
alignItems: 'center',
|
||||||
: '#db2828',
|
gap: '0.5em',
|
||||||
fontWeight: '500',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{statusState?.status?.github_oauth
|
<i className='github icon'></i>
|
||||||
? '已启用'
|
<span style={{ fontWeight: 'bold' }}>
|
||||||
: '未启用'}
|
GitHub 身份验证:
|
||||||
</span>
|
</span>
|
||||||
</p>
|
<span
|
||||||
<p
|
style={{
|
||||||
style={{
|
color: statusState?.status?.github_oauth
|
||||||
display: 'flex',
|
? '#21ba45'
|
||||||
alignItems: 'center',
|
: '#db2828',
|
||||||
gap: '0.5em',
|
fontWeight: '500',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className='wechat icon'></i>
|
{statusState?.status?.github_oauth
|
||||||
<span style={{ fontWeight: 'bold' }}>
|
? '已启用'
|
||||||
微信身份验证:
|
: '未启用'}
|
||||||
</span>
|
</span>
|
||||||
<span
|
</p>
|
||||||
|
<p
|
||||||
style={{
|
style={{
|
||||||
color: statusState?.status?.wechat_login
|
display: 'flex',
|
||||||
? '#21ba45'
|
alignItems: 'center',
|
||||||
: '#db2828',
|
gap: '0.5em',
|
||||||
fontWeight: '500',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{statusState?.status?.wechat_login
|
<i className='wechat icon'></i>
|
||||||
? '已启用'
|
<span style={{ fontWeight: 'bold' }}>
|
||||||
: '未启用'}
|
微信身份验证:
|
||||||
</span>
|
</span>
|
||||||
</p>
|
<span
|
||||||
<p
|
style={{
|
||||||
style={{
|
color: statusState?.status?.wechat_login
|
||||||
display: 'flex',
|
? '#21ba45'
|
||||||
alignItems: 'center',
|
: '#db2828',
|
||||||
gap: '0.5em',
|
fontWeight: '500',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className='shield alternate icon'></i>
|
{statusState?.status?.wechat_login
|
||||||
<span style={{ fontWeight: 'bold' }}>
|
? '已启用'
|
||||||
Turnstile 校验:
|
: '未启用'}
|
||||||
</span>
|
</span>
|
||||||
<span
|
</p>
|
||||||
|
<p
|
||||||
style={{
|
style={{
|
||||||
color: statusState?.status?.turnstile_check
|
display: 'flex',
|
||||||
? '#21ba45'
|
alignItems: 'center',
|
||||||
: '#db2828',
|
gap: '0.5em',
|
||||||
fontWeight: '500',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{statusState?.status?.turnstile_check
|
<i className='shield alternate icon'></i>
|
||||||
? '已启用'
|
<span style={{ fontWeight: 'bold' }}>
|
||||||
: '未启用'}
|
Turnstile 校验:
|
||||||
</span>
|
</span>
|
||||||
</p>
|
<span
|
||||||
</Card.Description>
|
style={{
|
||||||
</Card.Content>
|
color: statusState?.status?.turnstile_check
|
||||||
</Card>
|
? '#21ba45'
|
||||||
</Grid.Column>
|
: '#db2828',
|
||||||
</Grid>
|
fontWeight: '500',
|
||||||
</Card.Content>
|
}}
|
||||||
</Card>
|
>
|
||||||
|
{statusState?.status?.turnstile_check
|
||||||
|
? '已启用'
|
||||||
|
: '未启用'}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</Card.Description>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>{' '}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{homePageContent.startsWith('https://') ? (
|
{homePageContent.startsWith('https://') ? (
|
||||||
@@ -283,7 +287,7 @@ const Home = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user