Compare commits

...

6 Commits

Author SHA1 Message Date
1808837298@qq.com
e8845ce1de feat: log channel status update time 2024-06-16 14:53:39 +08:00
1808837298@qq.com
b069056bda fix panic 2024-06-16 13:38:36 +08:00
1808837298@qq.com
954fa879dc fix: claude usage 2024-06-15 01:30:51 +08:00
1808837298@qq.com
4eb6217bc0 feat: only update midjourney task on master node 2024-06-15 01:11:03 +08:00
1808837298@qq.com
eb79880502 feat: 记录自动禁用原因 (close #300) 2024-06-15 01:11:03 +08:00
1808837298@qq.com
692455ef2a fix: 删除用户改为注销 2024-05-31 15:30:22 +08:00
8 changed files with 98 additions and 68 deletions

View File

@@ -19,6 +19,9 @@ import (
)
func UpdateMidjourneyTaskBulk() {
if !common.IsMasterNode {
return
}
//imageModel := "midjourney"
ctx := context.TODO()
for {

View File

@@ -1,6 +1,7 @@
package model
import (
"encoding/json"
"gorm.io/gorm"
"one-api/common"
)
@@ -29,6 +30,31 @@ type Channel struct {
StatusCodeMapping *string `json:"status_code_mapping" gorm:"type:varchar(1024);default:''"`
Priority *int64 `json:"priority" gorm:"bigint;default:0"`
AutoBan *int `json:"auto_ban" gorm:"default:1"`
OtherInfo string `json:"other_info"`
}
func (channel *Channel) GetOtherInfo() map[string]interface{} {
otherInfo := make(map[string]interface{})
if channel.OtherInfo != "" {
err := json.Unmarshal([]byte(channel.OtherInfo), &otherInfo)
if err != nil {
common.SysError("failed to unmarshal other info: " + err.Error())
}
}
return otherInfo
}
func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) {
otherInfoBytes, err := json.Marshal(otherInfo)
if err != nil {
common.SysError("failed to marshal other info: " + err.Error())
return
}
channel.OtherInfo = string(otherInfoBytes)
}
func (channel *Channel) Save() error {
return DB.Save(channel).Error
}
func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Channel, error) {
@@ -213,15 +239,31 @@ func (channel *Channel) Delete() error {
return err
}
func UpdateChannelStatusById(id int, status int) {
func UpdateChannelStatusById(id int, status int, reason string) {
err := UpdateAbilityStatus(id, status == common.ChannelStatusEnabled)
if err != nil {
common.SysError("failed to update ability status: " + err.Error())
}
err = DB.Model(&Channel{}).Where("id = ?", id).Update("status", status).Error
channel, err := GetChannelById(id, true)
if err != nil {
common.SysError("failed to update channel status: " + err.Error())
// find channel by id error, directly update status
err = DB.Model(&Channel{}).Where("id = ?", id).Update("status", status).Error
if err != nil {
common.SysError("failed to update channel status: " + err.Error())
}
} else {
// find channel by id success, update status and other info
info := channel.GetOtherInfo()
info["status_reason"] = reason
info["status_time"] = common.GetTimestamp()
channel.SetOtherInfo(info)
channel.Status = status
err = channel.Save()
if err != nil {
common.SysError("failed to update channel status: " + err.Error())
}
}
}
func UpdateChannelUsedQuota(id int, quota int) {

View File

@@ -337,6 +337,9 @@ func claudeStreamHandler(requestMode int, modelName string, promptTokens int, c
if requestMode == RequestModeCompletion {
usage, _ = service.ResponseText2Usage(responseText, modelName, promptTokens)
} else {
if usage.PromptTokens == 0 {
usage.PromptTokens = promptTokens
}
if usage.CompletionTokens == 0 {
usage, _ = service.ResponseText2Usage(responseText, modelName, usage.PromptTokens)
}

View File

@@ -11,14 +11,14 @@ import (
// disable & notify
func DisableChannel(channelId int, channelName string, reason string) {
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled, reason)
subject := fmt.Sprintf("通道「%s」#%d已被禁用", channelName, channelId)
content := fmt.Sprintf("通道「%s」#%d已被禁用原因%s", channelName, channelId, reason)
notifyRootUser(subject, content)
}
func EnableChannel(channelId int, channelName string) {
model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled)
model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled, "")
subject := fmt.Sprintf("通道「%s」#%d已被启用", channelName, channelId)
content := fmt.Sprintf("通道「%s」#%d已被启用", channelName, channelId)
notifyRootUser(subject, content)

View File

@@ -96,7 +96,23 @@ const ChannelsTable = () => {
title: '状态',
dataIndex: 'status',
render: (text, record, index) => {
return <div>{renderStatus(text)}</div>;
if (text === 3) {
if (record.other_info === '') {
record.other_info = '{}'
}
let otherInfo = JSON.parse(record.other_info);
let reason = otherInfo['status_reason'];
let time = otherInfo['status_time'];
return (
<div>
<Tooltip content={'原因:' + reason + ',时间:' + timestamp2string(time)}>
{renderStatus(text)}
</Tooltip>
</div>
);
} else {
return renderStatus(text);
}
},
},
{

View File

@@ -225,6 +225,14 @@ const TokensTable = () => {
onOpenLink('next-mj', record.key);
},
},
{
node: 'item',
key: 'lobe',
name: 'Lobe Chat',
onClick: () => {
onOpenLink('lobe', record.key);
},
},
{
node: 'item',
key: 'ama',
@@ -377,51 +385,6 @@ const TokensTable = () => {
await loadTokens(activePage - 1);
};
const onCopy = async (type, key) => {
let status = localStorage.getItem('status');
let serverAddress = '';
if (status) {
status = JSON.parse(status);
serverAddress = status.server_address;
}
if (serverAddress === '') {
serverAddress = window.location.origin;
}
let encodedServerAddress = encodeURIComponent(serverAddress);
const nextLink = localStorage.getItem('chat_link');
const mjLink = localStorage.getItem('chat_link2');
let nextUrl;
if (nextLink) {
nextUrl =
nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
} else {
nextUrl = `https://chat.oneapi.pro/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
}
let url;
switch (type) {
case 'ama':
url =
mjLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
break;
case 'opencat':
url = `opencat://team/join?domain=${encodedServerAddress}&token=sk-${key}`;
break;
case 'next':
url = nextUrl;
break;
default:
url = `sk-${key}`;
}
// if (await copy(url)) {
// showSuccess('已复制到剪贴板!');
// } else {
// showWarning('无法复制到剪贴板,请手动复制,已将令牌填入搜索框。');
// setSearchKeyword(url);
// }
};
const copyText = async (text) => {
if (await copy(text)) {
showSuccess('已复制到剪贴板!');
@@ -461,6 +424,9 @@ const TokensTable = () => {
case 'opencat':
url = `opencat://team/join?domain=${encodedServerAddress}&token=sk-${key}`;
break;
case 'lobe':
url = `https://chat-preview.lobehub.com/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${encodedServerAddress}"}}}`;
break;
case 'next-mj':
url =
mjLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;

View File

@@ -208,23 +208,23 @@ const UsersTable = () => {
>
编辑
</Button>
<Popconfirm
title='确定是否要注销此用户?'
content='相当于删除用户,此修改将不可逆'
okType={'danger'}
position={'left'}
onConfirm={() => {
manageUser(record.username, 'delete', record).then(() => {
removeRecord(record.id);
});
}}
>
<Button theme='light' type='danger' style={{ marginRight: 1 }}>
注销
</Button>
</Popconfirm>
</>
)}
<Popconfirm
title='确定是否要删除此用户?'
content='硬删除,此修改将不可逆'
okType={'danger'}
position={'left'}
onConfirm={() => {
manageUser(record.username, 'delete', record).then(() => {
removeRecord(record.id);
});
}}
>
<Button theme='light' type='danger' style={{ marginRight: 1 }}>
删除
</Button>
</Popconfirm>
</div>
),
},
@@ -253,13 +253,13 @@ const UsersTable = () => {
};
const removeRecord = (key) => {
console.log(key);
let newDataSource = [...users];
if (key != null) {
let idx = newDataSource.findIndex((data) => data.id === key);
if (idx > -1) {
newDataSource.splice(idx, 1);
// update deletedAt
newDataSource[idx].DeletedAt = new Date();
setUsers(newDataSource);
}
}