Compare commits

..

9 Commits

Author SHA1 Message Date
JustSong
dc94765d32 fix: do not print response to console 2023-06-18 00:22:22 +08:00
JustSong
1cb1f727c0 fix: fix the shared field is modified 2023-06-18 00:20:06 +08:00
JustSong
d97640374c feat: able to add chat page link now (close #70) 2023-06-17 23:51:56 +08:00
JustSong
ba89abedf0 docs: update README 2023-06-17 23:18:27 +08:00
JustSong
a680b1b8b7 docs: update issue template 2023-06-17 22:57:46 +08:00
JustSong
b3b7d0a0ea docs: update README 2023-06-17 22:56:12 +08:00
JustSong
8e805e23bc chore: update prompt 2023-06-17 19:33:25 +08:00
JustSong
bcbfacc04a fix: reduce the table size (close #174) 2023-06-17 19:23:25 +08:00
JustSong
5531e21526 docs: update README (close #175) 2023-06-17 19:08:13 +08:00
16 changed files with 102 additions and 34 deletions

View File

@@ -10,6 +10,7 @@ assignees: ''
**例行检查** **例行检查**
+ [ ] 我已确认目前没有类似 issue + [ ] 我已确认目前没有类似 issue
+ [ ] 我已确认我已升级到最新版本 + [ ] 我已确认我已升级到最新版本
+ [ ] 我已完整查看过项目 README尤其是常见问题部分
+ [ ] 我理解并愿意跟进此 issue协助测试和提供反馈 + [ ] 我理解并愿意跟进此 issue协助测试和提供反馈
+ [ ] 我理解并认可上述内容,并理解项目维护者精力有限,不遵循规则的 issue 可能会被无视或直接关闭 + [ ] 我理解并认可上述内容,并理解项目维护者精力有限,不遵循规则的 issue 可能会被无视或直接关闭

View File

@@ -155,6 +155,12 @@ sudo service nginx restart
环境变量的具体使用方法详见[此处](#环境变量)。 环境变量的具体使用方法详见[此处](#环境变量)。
### 宝塔部署教程
详见[#175](https://github.com/songquanpeng/one-api/issues/175)。
如果部署后访问出现空白页面,详见[#97](https://github.com/songquanpeng/one-api/issues/97)。
### 部署第三方服务配合 One API 使用 ### 部署第三方服务配合 One API 使用
> 欢迎 PR 添加更多示例。 > 欢迎 PR 添加更多示例。
@@ -256,14 +262,18 @@ https://openai.justsong.cn
![token](https://user-images.githubusercontent.com/39998050/233837971-dab488b7-6d96-43af-b640-a168e8d1c9bf.png) ![token](https://user-images.githubusercontent.com/39998050/233837971-dab488b7-6d96-43af-b640-a168e8d1c9bf.png)
## 常见问题 ## 常见问题
1. 账户额度足够为什么提示额度不足 1. 额度是什么?怎么计算的
+ 额度 = token * 倍率
+ 倍率包括分组的倍率,以及补全的倍率。
2. 账户额度足够为什么提示额度不足?
+ 请检查你的令牌额度是否足够,这个和账户额度是分开的。 + 请检查你的令牌额度是否足够,这个和账户额度是分开的。
+ 令牌额度仅供用户设置最大使用量,用户可自由设置。 + 令牌额度仅供用户设置最大使用量,用户可自由设置。
2. 宝塔部署后访问出现空白页面?
+ 自动配置的问题,详见[#97](https://github.com/songquanpeng/one-api/issues/97)。
3. 提示无可用渠道? 3. 提示无可用渠道?
+ 请检查的用户分组和渠道分组设置。 + 请检查的用户分组和渠道分组设置。
+ 以及渠道的模型设置。 + 以及渠道的模型设置。
4. 渠道测试报错:`invalid character '<' looking for beginning of value`
+ 这是因为返回值不是合法的 JSON而是一个 HTML 页面。
+ 大概率是你的部署站的 IP 或代理的节点被 CloudFlare 封禁了。
## 注意 ## 注意
本项目为开源项目,请在遵循 OpenAI 的[使用条款](https://openai.com/policies/terms-of-use)以及法律法规的情况下使用,不得用于非法用途。 本项目为开源项目,请在遵循 OpenAI 的[使用条款](https://openai.com/policies/terms-of-use)以及法律法规的情况下使用,不得用于非法用途。

View File

@@ -14,6 +14,7 @@ var ServerAddress = "http://localhost:3000"
var Footer = "" var Footer = ""
var Logo = "" var Logo = ""
var TopUpLink = "" var TopUpLink = ""
var ChatLink = ""
var UsingSQLite = false var UsingSQLite = false

View File

@@ -143,7 +143,6 @@ func updateChannelAPI2GPTBalance(channel *model.Channel) (float64, error) {
} }
response := API2GPTUsageResponse{} response := API2GPTUsageResponse{}
err = json.Unmarshal(body, &response) err = json.Unmarshal(body, &response)
fmt.Print(response)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@@ -14,12 +14,12 @@ import (
"time" "time"
) )
func testChannel(channel *model.Channel, request *ChatRequest) error { func testChannel(channel *model.Channel, request ChatRequest) error {
if request.Model == "" { switch channel.Type {
case common.ChannelTypeAzure:
request.Model = "gpt-35-turbo"
default:
request.Model = "gpt-3.5-turbo" request.Model = "gpt-3.5-turbo"
if channel.Type == common.ChannelTypeAzure {
request.Model = "gpt-35-turbo"
}
} }
requestURL := common.ChannelBaseURLs[channel.Type] requestURL := common.ChannelBaseURLs[channel.Type]
if channel.Type == common.ChannelTypeAzure { if channel.Type == common.ChannelTypeAzure {
@@ -97,7 +97,7 @@ func TestChannel(c *gin.Context) {
} }
testRequest := buildTestRequest(c) testRequest := buildTestRequest(c)
tik := time.Now() tik := time.Now()
err = testChannel(channel, testRequest) err = testChannel(channel, *testRequest)
tok := time.Now() tok := time.Now()
milliseconds := tok.Sub(tik).Milliseconds() milliseconds := tok.Sub(tik).Milliseconds()
go channel.UpdateResponseTime(milliseconds) go channel.UpdateResponseTime(milliseconds)
@@ -165,7 +165,7 @@ func testAllChannels(c *gin.Context) error {
continue continue
} }
tik := time.Now() tik := time.Now()
err := testChannel(channel, testRequest) err := testChannel(channel, *testRequest)
tok := time.Now() tok := time.Now()
milliseconds := tok.Sub(tik).Milliseconds() milliseconds := tok.Sub(tik).Milliseconds()
if err != nil || milliseconds > disableThreshold { if err != nil || milliseconds > disableThreshold {

View File

@@ -28,6 +28,7 @@ func GetStatus(c *gin.Context) {
"turnstile_check": common.TurnstileCheckEnabled, "turnstile_check": common.TurnstileCheckEnabled,
"turnstile_site_key": common.TurnstileSiteKey, "turnstile_site_key": common.TurnstileSiteKey,
"top_up_link": common.TopUpLink, "top_up_link": common.TopUpLink,
"chat_link": common.ChatLink,
}, },
}) })
return return

View File

@@ -63,6 +63,7 @@ func InitOptionMap() {
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString() common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString() common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
common.OptionMap["TopUpLink"] = common.TopUpLink common.OptionMap["TopUpLink"] = common.TopUpLink
common.OptionMap["ChatLink"] = common.ChatLink
common.OptionMapRWMutex.Unlock() common.OptionMapRWMutex.Unlock()
loadOptionsFromDatabase() loadOptionsFromDatabase()
} }
@@ -191,6 +192,8 @@ func updateOptionMap(key string, value string) (err error) {
err = common.UpdateGroupRatioByJSONString(value) err = common.UpdateGroupRatioByJSONString(value)
case "TopUpLink": case "TopUpLink":
common.TopUpLink = value common.TopUpLink = value
case "ChatLink":
common.ChatLink = value
case "ChannelDisableThreshold": case "ChannelDisableThreshold":
common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64) common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64)
} }

View File

@@ -23,6 +23,7 @@ import Redemption from './pages/Redemption';
import EditRedemption from './pages/Redemption/EditRedemption'; import EditRedemption from './pages/Redemption/EditRedemption';
import TopUp from './pages/TopUp'; import TopUp from './pages/TopUp';
import Log from './pages/Log'; import Log from './pages/Log';
import Chat from './pages/Chat';
const Home = lazy(() => import('./pages/Home')); const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About')); const About = lazy(() => import('./pages/About'));
@@ -47,6 +48,11 @@ function App() {
localStorage.setItem('system_name', data.system_name); localStorage.setItem('system_name', data.system_name);
localStorage.setItem('logo', data.logo); localStorage.setItem('logo', data.logo);
localStorage.setItem('footer_html', data.footer_html); localStorage.setItem('footer_html', data.footer_html);
if (data.chat_link) {
localStorage.setItem('chat_link', data.chat_link);
} else {
localStorage.removeItem('chat_link');
}
if ( if (
data.version !== process.env.REACT_APP_VERSION && data.version !== process.env.REACT_APP_VERSION &&
data.version !== 'v0.0.0' && data.version !== 'v0.0.0' &&
@@ -267,6 +273,14 @@ function App() {
</Suspense> </Suspense>
} }
/> />
<Route
path='/chat'
element={
<Suspense fallback={<Loading></Loading>}>
<Chat />
</Suspense>
}
/>
<Route path='*' element={NotFound} /> <Route path='*' element={NotFound} />
</Routes> </Routes>
); );

View File

@@ -262,7 +262,7 @@ const ChannelsTable = () => {
/> />
</Form> </Form>
<Table basic> <Table basic compact size='small'>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell

View File

@@ -7,57 +7,65 @@ import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../
import '../index.css'; import '../index.css';
// Header Buttons // Header Buttons
const headerButtons = [ let headerButtons = [
{ {
name: '首页', name: '首页',
to: '/', to: '/',
icon: 'home', icon: 'home'
}, },
{ {
name: '渠道', name: '渠道',
to: '/channel', to: '/channel',
icon: 'sitemap', icon: 'sitemap',
admin: true, admin: true
}, },
{ {
name: '令牌', name: '令牌',
to: '/token', to: '/token',
icon: 'key', icon: 'key'
}, },
{ {
name: '兑换', name: '兑换',
to: '/redemption', to: '/redemption',
icon: 'dollar sign', icon: 'dollar sign',
admin: true, admin: true
}, },
{ {
name: '充值', name: '充值',
to: '/topup', to: '/topup',
icon: 'cart', icon: 'cart'
}, },
{ {
name: '用户', name: '用户',
to: '/user', to: '/user',
icon: 'user', icon: 'user',
admin: true, admin: true
}, },
{ {
name: '日志', name: '日志',
to: '/log', to: '/log',
icon: 'book', icon: 'book'
}, },
{ {
name: '设置', name: '设置',
to: '/setting', to: '/setting',
icon: 'setting', icon: 'setting'
}, },
{ {
name: '关于', name: '关于',
to: '/about', to: '/about',
icon: 'info circle', icon: 'info circle'
}, }
]; ];
if (localStorage.getItem('chat_link')) {
headerButtons.splice(1, 0, {
name: '聊天',
to: '/chat',
icon: 'comments'
});
}
const Header = () => { const Header = () => {
const [userState, userDispatch] = useContext(UserContext); const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate(); let navigate = useNavigate();
@@ -112,11 +120,11 @@ const Header = () => {
style={ style={
showSidebar showSidebar
? { ? {
borderBottom: 'none', borderBottom: 'none',
marginBottom: '0', marginBottom: '0',
borderTop: 'none', borderTop: 'none',
height: '51px', height: '51px'
} }
: { borderTop: 'none', height: '52px' } : { borderTop: 'none', height: '52px' }
} }
> >

View File

@@ -165,7 +165,7 @@ const OtherSetting = () => {
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={submitAbout}>保存关于</Form.Button> <Form.Button onClick={submitAbout}>保存关于</Form.Button>
<Message>移除 One API 的版权标识必须首先获得授权后续版本将通过授权码强制执行</Message> <Message>移除 One API 的版权标识必须首先获得授权项目维护需要花费大量精力如果本项目对你有意义请主动支持本项目</Message>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.Input <Form.Input
label='页脚' label='页脚'

View File

@@ -152,7 +152,7 @@ const RedemptionsTable = () => {
/> />
</Form> </Form>
<Table basic> <Table basic compact size='small'>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell

View File

@@ -34,6 +34,7 @@ const SystemSetting = () => {
ModelRatio: '', ModelRatio: '',
GroupRatio: '', GroupRatio: '',
TopUpLink: '', TopUpLink: '',
ChatLink: '',
AutomaticDisableChannelEnabled: '', AutomaticDisableChannelEnabled: '',
ChannelDisableThreshold: 0, ChannelDisableThreshold: 0,
LogConsumeEnabled: '' LogConsumeEnabled: ''
@@ -109,7 +110,8 @@ const SystemSetting = () => {
name === 'PreConsumedQuota' || name === 'PreConsumedQuota' ||
name === 'ModelRatio' || name === 'ModelRatio' ||
name === 'GroupRatio' || name === 'GroupRatio' ||
name === 'TopUpLink' name === 'TopUpLink' ||
name === 'ChatLink'
) { ) {
setInputs((inputs) => ({ ...inputs, [name]: value })); setInputs((inputs) => ({ ...inputs, [name]: value }));
} else { } else {
@@ -155,6 +157,9 @@ const SystemSetting = () => {
if (originInputs['TopUpLink'] !== inputs.TopUpLink) { if (originInputs['TopUpLink'] !== inputs.TopUpLink) {
await updateOption('TopUpLink', inputs.TopUpLink); await updateOption('TopUpLink', inputs.TopUpLink);
} }
if (originInputs['ChatLink'] !== inputs.ChatLink) {
await updateOption('ChatLink', inputs.ChatLink);
}
}; };
const submitSMTP = async () => { const submitSMTP = async () => {
@@ -360,6 +365,15 @@ const SystemSetting = () => {
min='0' min='0'
placeholder='例如100' placeholder='例如100'
/> />
<Form.Input
label='聊天页面链接'
name='ChatLink'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.ChatLink}
type='link'
placeholder='例如 ChatGPT Next Web 的部署地址'
/>
</Form.Group> </Form.Group>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.TextArea <Form.TextArea

View File

@@ -161,7 +161,7 @@ const TokensTable = () => {
/> />
</Form> </Form>
<Table basic> <Table basic compact size='small'>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell

View File

@@ -156,7 +156,7 @@ const UsersTable = () => {
/> />
</Form> </Form>
<Table basic> <Table basic compact size='small'>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell
@@ -240,7 +240,9 @@ const UsersTable = () => {
/> />
</Table.Cell> </Table.Cell>
<Table.Cell>{renderGroup(user.group)}</Table.Cell> <Table.Cell>{renderGroup(user.group)}</Table.Cell>
<Table.Cell>{user.email ? renderText(user.email, 20) : '无'}</Table.Cell> <Table.Cell>
{user.email ? <Popup hoverable content={user.email} trigger={<span>{renderText(user.email, 24)}</span>} /> : '无'}
</Table.Cell>
<Table.Cell> <Table.Cell>
<Popup content='剩余额度' trigger={<Label>{renderNumber(user.quota)}</Label>} /> <Popup content='剩余额度' trigger={<Label>{renderNumber(user.quota)}</Label>} />
<Popup content='已用额度' trigger={<Label>{renderNumber(user.used_quota)}</Label>} /> <Popup content='已用额度' trigger={<Label>{renderNumber(user.used_quota)}</Label>} />

View File

@@ -0,0 +1,15 @@
import React from 'react';
const Chat = () => {
const chatLink = localStorage.getItem('chat_link');
return (
<iframe
src={chatLink}
style={{ width: '100%', height: '85vh', border: 'none' }}
/>
);
};
export default Chat;