mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 16:06:38 +08:00
feat: 令牌分组
This commit is contained in:
parent
5f3798053f
commit
052bc2075b
23
common/user_groups.go
Normal file
23
common/user_groups.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var UserUsableGroups = map[string]string{
|
||||||
|
"default": "默认分组",
|
||||||
|
"vip": "vip分组",
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserUsableGroups2JSONString() string {
|
||||||
|
jsonBytes, err := json.Marshal(UserUsableGroups)
|
||||||
|
if err != nil {
|
||||||
|
SysError("error marshalling user groups: " + err.Error())
|
||||||
|
}
|
||||||
|
return string(jsonBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUserUsableGroupsByJSONString(jsonStr string) error {
|
||||||
|
UserUsableGroups = make(map[string]string)
|
||||||
|
return json.Unmarshal([]byte(jsonStr), &UserUsableGroups)
|
||||||
|
}
|
@ -17,3 +17,18 @@ func GetGroups(c *gin.Context) {
|
|||||||
"data": groupNames,
|
"data": groupNames,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserGroups(c *gin.Context) {
|
||||||
|
usableGroups := make(map[string]string)
|
||||||
|
for groupName, _ := range common.GroupRatio {
|
||||||
|
// UserUsableGroups contains the groups that the user can use
|
||||||
|
if _, ok := common.UserUsableGroups[groupName]; ok {
|
||||||
|
usableGroups[groupName] = common.UserUsableGroups[groupName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"data": usableGroups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -135,6 +135,7 @@ func AddToken(c *gin.Context) {
|
|||||||
ModelLimitsEnabled: token.ModelLimitsEnabled,
|
ModelLimitsEnabled: token.ModelLimitsEnabled,
|
||||||
ModelLimits: token.ModelLimits,
|
ModelLimits: token.ModelLimits,
|
||||||
AllowIps: token.AllowIps,
|
AllowIps: token.AllowIps,
|
||||||
|
Group: token.Group,
|
||||||
}
|
}
|
||||||
err = cleanToken.Insert()
|
err = cleanToken.Insert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -223,6 +224,7 @@ func UpdateToken(c *gin.Context) {
|
|||||||
cleanToken.ModelLimitsEnabled = token.ModelLimitsEnabled
|
cleanToken.ModelLimitsEnabled = token.ModelLimitsEnabled
|
||||||
cleanToken.ModelLimits = token.ModelLimits
|
cleanToken.ModelLimits = token.ModelLimits
|
||||||
cleanToken.AllowIps = token.AllowIps
|
cleanToken.AllowIps = token.AllowIps
|
||||||
|
cleanToken.Group = token.Group
|
||||||
}
|
}
|
||||||
err = cleanToken.Update()
|
err = cleanToken.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -176,6 +176,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
c.Set("token_model_limit_enabled", false)
|
c.Set("token_model_limit_enabled", false)
|
||||||
}
|
}
|
||||||
c.Set("allow_ips", token.GetIpLimitsMap())
|
c.Set("allow_ips", token.GetIpLimitsMap())
|
||||||
|
c.Set("token_group", token.Group)
|
||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
if model.IsAdmin(token.UserId) {
|
if model.IsAdmin(token.UserId) {
|
||||||
c.Set("specific_channel_id", parts[1])
|
c.Set("specific_channel_id", parts[1])
|
||||||
|
@ -39,6 +39,15 @@ func Distribute() func(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
userGroup, _ := model.CacheGetUserGroup(userId)
|
userGroup, _ := model.CacheGetUserGroup(userId)
|
||||||
|
tokenGroup := c.GetString("token_group")
|
||||||
|
if tokenGroup != "" {
|
||||||
|
// check group in common.GroupRatio
|
||||||
|
if _, ok := common.GroupRatio[tokenGroup]; !ok {
|
||||||
|
abortWithOpenAiMessage(c, http.StatusForbidden, fmt.Sprintf("分组 %s 已被禁用", tokenGroup))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userGroup = tokenGroup
|
||||||
|
}
|
||||||
c.Set("group", userGroup)
|
c.Set("group", userGroup)
|
||||||
if ok {
|
if ok {
|
||||||
id, err := strconv.Atoi(channelId.(string))
|
id, err := strconv.Atoi(channelId.(string))
|
||||||
|
@ -86,6 +86,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
|
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
|
||||||
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
|
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
|
||||||
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
|
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
|
||||||
|
common.OptionMap["UserUsableGroups"] = common.UserUsableGroups2JSONString()
|
||||||
common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString()
|
common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString()
|
||||||
common.OptionMap["TopUpLink"] = common.TopUpLink
|
common.OptionMap["TopUpLink"] = common.TopUpLink
|
||||||
common.OptionMap["ChatLink"] = common.ChatLink
|
common.OptionMap["ChatLink"] = common.ChatLink
|
||||||
@ -303,6 +304,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
err = common.UpdateModelRatioByJSONString(value)
|
err = common.UpdateModelRatioByJSONString(value)
|
||||||
case "GroupRatio":
|
case "GroupRatio":
|
||||||
err = common.UpdateGroupRatioByJSONString(value)
|
err = common.UpdateGroupRatioByJSONString(value)
|
||||||
|
case "UserUsableGroups":
|
||||||
|
err = common.UpdateUserUsableGroupsByJSONString(value)
|
||||||
case "CompletionRatio":
|
case "CompletionRatio":
|
||||||
err = common.UpdateCompletionRatioByJSONString(value)
|
err = common.UpdateCompletionRatioByJSONString(value)
|
||||||
case "ModelPrice":
|
case "ModelPrice":
|
||||||
|
@ -25,6 +25,7 @@ type Token struct {
|
|||||||
ModelLimits string `json:"model_limits" gorm:"type:varchar(1024);default:''"`
|
ModelLimits string `json:"model_limits" gorm:"type:varchar(1024);default:''"`
|
||||||
AllowIps *string `json:"allow_ips" gorm:"default:''"`
|
AllowIps *string `json:"allow_ips" gorm:"default:''"`
|
||||||
UsedQuota int `json:"used_quota" gorm:"default:0"` // used quota
|
UsedQuota int `json:"used_quota" gorm:"default:0"` // used quota
|
||||||
|
Group string `json:"group" gorm:"default:''"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +154,8 @@ func (token *Token) Insert() error {
|
|||||||
// Update Make sure your token's fields is completed, because this will update non-zero values
|
// Update Make sure your token's fields is completed, because this will update non-zero values
|
||||||
func (token *Token) Update() error {
|
func (token *Token) Update() error {
|
||||||
var err error
|
var err error
|
||||||
err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota", "model_limits_enabled", "model_limits", "allow_ips").Updates(token).Error
|
err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota",
|
||||||
|
"model_limits_enabled", "model_limits", "allow_ips", "group").Updates(token).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
//userRoute.POST("/tokenlog", middleware.CriticalRateLimit(), controller.TokenLog)
|
//userRoute.POST("/tokenlog", middleware.CriticalRateLimit(), controller.TokenLog)
|
||||||
userRoute.GET("/logout", controller.Logout)
|
userRoute.GET("/logout", controller.Logout)
|
||||||
userRoute.GET("/epay/notify", controller.EpayNotify)
|
userRoute.GET("/epay/notify", controller.EpayNotify)
|
||||||
|
userRoute.GET("/groups", controller.GetUserGroups)
|
||||||
|
|
||||||
selfRoute := userRoute.Group("/")
|
selfRoute := userRoute.Group("/")
|
||||||
selfRoute.Use(middleware.UserAuth())
|
selfRoute.Use(middleware.UserAuth())
|
||||||
|
@ -23,6 +23,7 @@ const OperationSetting = () => {
|
|||||||
CompletionRatio: '',
|
CompletionRatio: '',
|
||||||
ModelPrice: '',
|
ModelPrice: '',
|
||||||
GroupRatio: '',
|
GroupRatio: '',
|
||||||
|
UserUsableGroups: '',
|
||||||
TopUpLink: '',
|
TopUpLink: '',
|
||||||
ChatLink: '',
|
ChatLink: '',
|
||||||
ChatLink2: '', // 添加的新状态变量
|
ChatLink2: '', // 添加的新状态变量
|
||||||
@ -62,6 +63,7 @@ const OperationSetting = () => {
|
|||||||
if (
|
if (
|
||||||
item.key === 'ModelRatio' ||
|
item.key === 'ModelRatio' ||
|
||||||
item.key === 'GroupRatio' ||
|
item.key === 'GroupRatio' ||
|
||||||
|
item.key === 'UserUsableGroups' ||
|
||||||
item.key === 'CompletionRatio' ||
|
item.key === 'CompletionRatio' ||
|
||||||
item.key === 'ModelPrice'
|
item.key === 'ModelPrice'
|
||||||
) {
|
) {
|
||||||
|
@ -8,14 +8,14 @@ import {
|
|||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
|
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
import { renderQuota } from '../helpers/render';
|
import {renderGroup, renderQuota} from '../helpers/render';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Form,
|
Form,
|
||||||
Modal,
|
Modal,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Popover,
|
Popover, Space,
|
||||||
SplitButtonGroup,
|
SplitButtonGroup,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
@ -119,7 +119,12 @@ const TokensTable = () => {
|
|||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return <div>{renderStatus(text, record.model_limits_enabled)}</div>;
|
return <div>
|
||||||
|
<Space>
|
||||||
|
{renderStatus(text, record.model_limits_enabled)}
|
||||||
|
{renderGroup(record.group)}
|
||||||
|
</Space>
|
||||||
|
</div>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -15,8 +15,8 @@ export function renderText(text, limit) {
|
|||||||
export function renderGroup(group) {
|
export function renderGroup(group) {
|
||||||
if (group === '') {
|
if (group === '') {
|
||||||
return (
|
return (
|
||||||
<Tag size='large' key='default'>
|
<Tag size='large' key='default' color={stringToColor('default')}>
|
||||||
unknown
|
default
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ export default function SettingsMagnification(props) {
|
|||||||
ModelPrice: '',
|
ModelPrice: '',
|
||||||
ModelRatio: '',
|
ModelRatio: '',
|
||||||
CompletionRatio: '',
|
CompletionRatio: '',
|
||||||
GroupRatio: ''
|
GroupRatio: '',
|
||||||
|
UserUsableGroups: ''
|
||||||
});
|
});
|
||||||
const refForm = useRef();
|
const refForm = useRef();
|
||||||
const [inputsRow, setInputsRow] = useState(inputs);
|
const [inputsRow, setInputsRow] = useState(inputs);
|
||||||
@ -213,6 +214,33 @@ export default function SettingsMagnification(props) {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={'用户可选分组'}
|
||||||
|
extraText={''}
|
||||||
|
placeholder={'为一个 JSON 文本,键为分组名称,值为倍率'}
|
||||||
|
field={'UserUsableGroups'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => {
|
||||||
|
return verifyJSON(value);
|
||||||
|
},
|
||||||
|
message: '不是合法的 JSON 字符串'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) =>
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
UserUsableGroups: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Form.Section>
|
</Form.Section>
|
||||||
</Form>
|
</Form>
|
||||||
<Space>
|
<Space>
|
||||||
|
@ -35,6 +35,7 @@ const EditToken = (props) => {
|
|||||||
model_limits_enabled: false,
|
model_limits_enabled: false,
|
||||||
model_limits: [],
|
model_limits: [],
|
||||||
allow_ips: '',
|
allow_ips: '',
|
||||||
|
group: '',
|
||||||
};
|
};
|
||||||
const [inputs, setInputs] = useState(originInputs);
|
const [inputs, setInputs] = useState(originInputs);
|
||||||
const {
|
const {
|
||||||
@ -44,10 +45,12 @@ const EditToken = (props) => {
|
|||||||
unlimited_quota,
|
unlimited_quota,
|
||||||
model_limits_enabled,
|
model_limits_enabled,
|
||||||
model_limits,
|
model_limits,
|
||||||
allow_ips
|
allow_ips,
|
||||||
|
group
|
||||||
} = inputs;
|
} = inputs;
|
||||||
// const [visible, setVisible] = useState(false);
|
// const [visible, setVisible] = useState(false);
|
||||||
const [models, setModels] = useState({});
|
const [models, setModels] = useState({});
|
||||||
|
const [groups, setGroups] = useState([]);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const handleInputChange = (name, value) => {
|
const handleInputChange = (name, value) => {
|
||||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||||
@ -88,6 +91,22 @@ const EditToken = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadGroups = async () => {
|
||||||
|
let res = await API.get(`/api/user/groups`);
|
||||||
|
const { success, message, data } = res.data;
|
||||||
|
if (success) {
|
||||||
|
// return data is a map, key is group name, value is group description
|
||||||
|
// label is group description, value is group name
|
||||||
|
let localGroupOptions = Object.keys(data).map((group) => ({
|
||||||
|
label: data[group],
|
||||||
|
value: group,
|
||||||
|
}));
|
||||||
|
setGroups(localGroupOptions);
|
||||||
|
} else {
|
||||||
|
showError(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadToken = async () => {
|
const loadToken = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let res = await API.get(`/api/token/${props.editingToken.id}`);
|
let res = await API.get(`/api/token/${props.editingToken.id}`);
|
||||||
@ -120,6 +139,7 @@ const EditToken = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
loadModels();
|
loadModels();
|
||||||
|
loadGroups();
|
||||||
}, [isEdit]);
|
}, [isEdit]);
|
||||||
|
|
||||||
// 新增 state 变量 tokenCount 来记录用户想要创建的令牌数量,默认为 1
|
// 新增 state 变量 tokenCount 来记录用户想要创建的令牌数量,默认为 1
|
||||||
@ -253,7 +273,7 @@ const EditToken = (props) => {
|
|||||||
>
|
>
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Input
|
<Input
|
||||||
style={{ marginTop: 20 }}
|
style={{marginTop: 20}}
|
||||||
label='名称'
|
label='名称'
|
||||||
name='name'
|
name='name'
|
||||||
placeholder={'请输入名称'}
|
placeholder={'请输入名称'}
|
||||||
@ -262,7 +282,7 @@ const EditToken = (props) => {
|
|||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
required={!isEdit}
|
required={!isEdit}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider/>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
label='过期时间'
|
label='过期时间'
|
||||||
name='expired_time'
|
name='expired_time'
|
||||||
@ -272,7 +292,7 @@ const EditToken = (props) => {
|
|||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
type='dateTime'
|
type='dateTime'
|
||||||
/>
|
/>
|
||||||
<div style={{ marginTop: 20 }}>
|
<div style={{marginTop: 20}}>
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
type={'tertiary'}
|
type={'tertiary'}
|
||||||
@ -309,18 +329,18 @@ const EditToken = (props) => {
|
|||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider/>
|
||||||
<Banner
|
<Banner
|
||||||
type={'warning'}
|
type={'warning'}
|
||||||
description={
|
description={
|
||||||
'注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。'
|
'注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。'
|
||||||
}
|
}
|
||||||
></Banner>
|
></Banner>
|
||||||
<div style={{ marginTop: 20 }}>
|
<div style={{marginTop: 20}}>
|
||||||
<Typography.Text>{`额度${renderQuotaWithPrompt(remain_quota)}`}</Typography.Text>
|
<Typography.Text>{`额度${renderQuotaWithPrompt(remain_quota)}`}</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
style={{ marginTop: 8 }}
|
style={{marginTop: 8}}
|
||||||
name='remain_quota'
|
name='remain_quota'
|
||||||
placeholder={'请输入额度'}
|
placeholder={'请输入额度'}
|
||||||
onChange={(value) => handleInputChange('remain_quota', value)}
|
onChange={(value) => handleInputChange('remain_quota', value)}
|
||||||
@ -329,23 +349,23 @@ const EditToken = (props) => {
|
|||||||
type='number'
|
type='number'
|
||||||
// position={'top'}
|
// position={'top'}
|
||||||
data={[
|
data={[
|
||||||
{ value: 500000, label: '1$' },
|
{value: 500000, label: '1$'},
|
||||||
{ value: 5000000, label: '10$' },
|
{value: 5000000, label: '10$'},
|
||||||
{ value: 25000000, label: '50$' },
|
{value: 25000000, label: '50$'},
|
||||||
{ value: 50000000, label: '100$' },
|
{value: 50000000, label: '100$'},
|
||||||
{ value: 250000000, label: '500$' },
|
{value: 250000000, label: '500$'},
|
||||||
{ value: 500000000, label: '1000$' },
|
{value: 500000000, label: '1000$'},
|
||||||
]}
|
]}
|
||||||
disabled={unlimited_quota}
|
disabled={unlimited_quota}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isEdit && (
|
{!isEdit && (
|
||||||
<>
|
<>
|
||||||
<div style={{ marginTop: 20 }}>
|
<div style={{marginTop: 20}}>
|
||||||
<Typography.Text>新建数量</Typography.Text>
|
<Typography.Text>新建数量</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
style={{ marginTop: 8 }}
|
style={{marginTop: 8}}
|
||||||
label='数量'
|
label='数量'
|
||||||
placeholder={'请选择或输入创建令牌的数量'}
|
placeholder={'请选择或输入创建令牌的数量'}
|
||||||
onChange={(value) => handleTokenCountChange(value)}
|
onChange={(value) => handleTokenCountChange(value)}
|
||||||
@ -354,10 +374,10 @@ const EditToken = (props) => {
|
|||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
type='number'
|
type='number'
|
||||||
data={[
|
data={[
|
||||||
{ value: 10, label: '10个' },
|
{value: 10, label: '10个'},
|
||||||
{ value: 20, label: '20个' },
|
{value: 20, label: '20个'},
|
||||||
{ value: 30, label: '30个' },
|
{value: 30, label: '30个'},
|
||||||
{ value: 100, label: '100个' },
|
{value: 100, label: '100个'},
|
||||||
]}
|
]}
|
||||||
disabled={unlimited_quota}
|
disabled={unlimited_quota}
|
||||||
/>
|
/>
|
||||||
@ -366,7 +386,7 @@ const EditToken = (props) => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginTop: 8 }}
|
style={{marginTop: 8}}
|
||||||
type={'warning'}
|
type={'warning'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUnlimitedQuota();
|
setUnlimitedQuota();
|
||||||
@ -375,8 +395,8 @@ const EditToken = (props) => {
|
|||||||
{unlimited_quota ? '取消无限额度' : '设为无限额度'}
|
{unlimited_quota ? '取消无限额度' : '设为无限额度'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider/>
|
||||||
<div style={{ marginTop: 10 }}>
|
<div style={{marginTop: 10}}>
|
||||||
<Typography.Text>IP白名单(请勿过度信任此功能)</Typography.Text>
|
<Typography.Text>IP白名单(请勿过度信任此功能)</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<TextArea
|
<TextArea
|
||||||
@ -387,9 +407,9 @@ const EditToken = (props) => {
|
|||||||
handleInputChange('allow_ips', value);
|
handleInputChange('allow_ips', value);
|
||||||
}}
|
}}
|
||||||
value={inputs.allow_ips}
|
value={inputs.allow_ips}
|
||||||
style={{ fontFamily: 'JetBrains Mono, Consolas' }}
|
style={{fontFamily: 'JetBrains Mono, Consolas'}}
|
||||||
/>
|
/>
|
||||||
<div style={{ marginTop: 10, display: 'flex' }}>
|
<div style={{marginTop: 10, display: 'flex'}}>
|
||||||
<Space>
|
<Space>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name='model_limits_enabled'
|
name='model_limits_enabled'
|
||||||
@ -405,7 +425,7 @@ const EditToken = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
style={{ marginTop: 8 }}
|
style={{marginTop: 8}}
|
||||||
placeholder={'请选择该渠道所支持的模型'}
|
placeholder={'请选择该渠道所支持的模型'}
|
||||||
name='models'
|
name='models'
|
||||||
required
|
required
|
||||||
@ -419,6 +439,23 @@ const EditToken = (props) => {
|
|||||||
optionList={models}
|
optionList={models}
|
||||||
disabled={!model_limits_enabled}
|
disabled={!model_limits_enabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div style={{marginTop: 10}}>
|
||||||
|
<Typography.Text>令牌分组,不选为默认分组</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
style={{marginTop: 8}}
|
||||||
|
placeholder={'令牌分组,不选为默认分组'}
|
||||||
|
name='gruop'
|
||||||
|
required
|
||||||
|
selection
|
||||||
|
onChange={(value) => {
|
||||||
|
handleInputChange('group', value);
|
||||||
|
}}
|
||||||
|
value={inputs.group}
|
||||||
|
autoComplete='new-password'
|
||||||
|
optionList={groups}
|
||||||
|
/>
|
||||||
</Spin>
|
</Spin>
|
||||||
</SideSheet>
|
</SideSheet>
|
||||||
</>
|
</>
|
||||||
|
Loading…
Reference in New Issue
Block a user