Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
47db16954f
@ -410,6 +410,12 @@ https://openai.justsong.cn
|
||||
+ 检查是否启用了 HTTPS,浏览器会拦截 HTTPS 域名下的 HTTP 请求。
|
||||
6. 报错:`当前分组负载已饱和,请稍后再试`
|
||||
+ 上游通道 429 了。
|
||||
7. 升级之后我的数据会丢失吗?
|
||||
+ 如果使用 MySQL,不会。
|
||||
+ 如果使用 SQLite,需要按照我所给的部署命令挂载 volume 持久化 one-api.db 数据库文件,否则容器重启后数据会丢失。
|
||||
8. 升级之前数据库需要做变更吗?
|
||||
+ 一般情况下不需要,系统将在初始化的时候自动调整。
|
||||
+ 如果需要的话,我会在更新日志中说明,并给出脚本。
|
||||
|
||||
## 相关项目
|
||||
* [FastGPT](https://github.com/labring/FastGPT): 基于 LLM 大语言模型的知识库问答系统
|
||||
|
@ -165,9 +165,10 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
ChannelStatusUnknown = 0
|
||||
ChannelStatusEnabled = 1 // don't use 0, 0 is the default value!
|
||||
ChannelStatusDisabled = 2 // also don't use 0
|
||||
ChannelStatusUnknown = 0
|
||||
ChannelStatusEnabled = 1 // don't use 0, 0 is the default value!
|
||||
ChannelStatusManuallyDisabled = 2 // also don't use 0
|
||||
ChannelStatusAutoDisabled = 3
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -209,7 +209,7 @@ func disableChannel(channelId int, channelName string, reason string) {
|
||||
if common.RootUserEmail == "" {
|
||||
common.RootUserEmail = model.GetRootUserEmail()
|
||||
}
|
||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusDisabled)
|
||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
||||
subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId)
|
||||
content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
||||
err := common.SendEmail(subject, common.RootUserEmail, content)
|
||||
|
@ -128,6 +128,23 @@ func DeleteChannel(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteManuallyDisabledChannel(c *gin.Context) {
|
||||
rows, err := model.DeleteChannelByStatus(common.ChannelStatusManuallyDisabled)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": rows,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateChannel(c *gin.Context) {
|
||||
channel := model.Channel{}
|
||||
err := c.ShouldBindJSON(&channel)
|
||||
|
@ -187,3 +187,8 @@ func updateChannelUsedQuota(id int, quota int) {
|
||||
common.SysError("failed to update channel used quota: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteChannelByStatus(status int64) (int64, error) {
|
||||
result := DB.Where("status = ?", status).Delete(&Channel{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
10
model/log.go
10
model/log.go
@ -8,14 +8,14 @@ import (
|
||||
)
|
||||
|
||||
type Log struct {
|
||||
Id int `json:"id"`
|
||||
Id int `json:"id;index:idx_created_at_id,priority:1"`
|
||||
UserId int `json:"user_id" gorm:"index"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"bigint;index"`
|
||||
Type int `json:"type" gorm:"index"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_id,priority:2;index:idx_created_at_type"`
|
||||
Type int `json:"type" gorm:"index:idx_created_at_type"`
|
||||
Content string `json:"content"`
|
||||
Username string `json:"username" gorm:"index;default:''"`
|
||||
Username string `json:"username" gorm:"index:index_username_model_name,priority:2;default:''"`
|
||||
TokenName string `json:"token_name" gorm:"index;default:''"`
|
||||
ModelName string `json:"model_name" gorm:"index;default:''"`
|
||||
ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"`
|
||||
Quota int `json:"quota" gorm:"default:0"`
|
||||
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
|
||||
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
|
||||
|
@ -82,6 +82,7 @@ func InitDB() (err error) {
|
||||
if !common.IsMasterNode {
|
||||
return nil
|
||||
}
|
||||
common.SysLog("database migration started")
|
||||
err = db.AutoMigrate(&Channel{})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -76,6 +76,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
channelRoute.GET("/update_balance/:id", controller.UpdateChannelBalance)
|
||||
channelRoute.POST("/", controller.AddChannel)
|
||||
channelRoute.PUT("/", controller.UpdateChannel)
|
||||
channelRoute.DELETE("/manually_disabled", controller.DeleteManuallyDisabledChannel)
|
||||
channelRoute.DELETE("/:id", controller.DeleteChannel)
|
||||
}
|
||||
tokenRoute := apiRouter.Group("/token")
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {Button, Form, Input, Label, Pagination, Popup, Table} from 'semantic-ui-react';
|
||||
import { Button, Form, Input, Label, Pagination, Popup, Table } from 'semantic-ui-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { API, showError, showInfo, showNotice, showSuccess, timestamp2string } from '../helpers';
|
||||
|
||||
@ -96,7 +96,7 @@ const ChannelsTable = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const manageChannel = async (id, action, idx, priority) => {
|
||||
const manageChannel = async (id, action, idx, value) => {
|
||||
let data = { id };
|
||||
let res;
|
||||
switch (action) {
|
||||
@ -112,10 +112,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;
|
||||
}
|
||||
@ -142,9 +152,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 (
|
||||
@ -216,6 +240,17 @@ const ChannelsTable = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAllManuallyDisabledChannels = async () => {
|
||||
const res = await API.delete(`/api/channel/manually_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;
|
||||
@ -343,10 +378,10 @@ const ChannelsTable = () => {
|
||||
余额
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
sortChannel('priority');
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
sortChannel('priority');
|
||||
}}
|
||||
>
|
||||
优先级
|
||||
</Table.HeaderCell>
|
||||
@ -390,18 +425,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>
|
||||
@ -481,6 +516,20 @@ const ChannelsTable = () => {
|
||||
</Button>
|
||||
<Button size='small' onClick={updateAllChannelsBalance}
|
||||
loading={loading || updatingBalance}>更新所有已启用通道余额</Button>
|
||||
<Popup
|
||||
trigger={
|
||||
<Button size='small' loading={loading}>
|
||||
删除所有手动禁用渠道
|
||||
</Button>
|
||||
}
|
||||
on='click'
|
||||
flowing
|
||||
hoverable
|
||||
>
|
||||
<Button size='small' loading={loading} negative onClick={deleteAllManuallyDisabledChannels}>
|
||||
确认删除
|
||||
</Button>
|
||||
</Popup>
|
||||
<Pagination
|
||||
floated='right'
|
||||
activePage={activePage}
|
||||
|
Loading…
Reference in New Issue
Block a user