feat: add token

This commit is contained in:
scarqin
2024-12-25 12:01:19 +08:00
parent 96183eb5df
commit 59ce2e0623
4 changed files with 726 additions and 703 deletions
@@ -673,6 +673,11 @@ export const PERMISSION_DEFINITION = [
granted: {
anyOf: [{ backend: ['project.permission_manager'] }]
}
},
'system.settings.ai_key_resource.manager': {
granted: {
anyOf: [{ backend: ['system.settings.ai_key_resource.manager'] }]
}
}
}
]
+432 -433
View File
@@ -1,497 +1,496 @@
import { GlobalNodeItem, ProxyHeaderItem, SystemApiTableListItem, SystemMemberTableListItem, SystemSubscriberTableListItem, SystemTableListItem } from "./type";
import { Input, TabsProps } from "antd";
import { MatchItem } from "@common/const/type";
import { ConfigField } from "@common/components/aoplatform/EditableTableWithModal";
import { frontendTimeSorter } from "@common/utils/dataTransfer";
import { COLUMNS_TITLE, PLACEHOLDER } from "@common/const/const";
import { ConfigField } from '@common/components/aoplatform/EditableTableWithModal'
import { COLUMNS_TITLE } from '@common/const/const'
import { MatchItem } from '@common/const/type'
import { frontendTimeSorter } from '@common/utils/dataTransfer'
import { Input, TabsProps } from 'antd'
import {
GlobalNodeItem,
ProxyHeaderItem,
SystemApiTableListItem,
SystemMemberTableListItem,
SystemSubscriberTableListItem,
SystemTableListItem
} from './type'
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { $t } from "@common/locales";
import { PageProColumns } from '@common/components/aoplatform/PageList'
export enum SubscribeEnum{
Rejected = 0,
Reviewing = 1,
Subscribed = 2,
Unsubscribed = 3,
CancelRequest = 4
export enum SubscribeEnum {
Rejected = 0,
Reviewing = 1,
Subscribed = 2,
Unsubscribed = 3,
CancelRequest = 4
}
export const SubscribeStatusColor= {
2: 'text-[#138913]', // 使用 Tailwind 的 Arbitrary Properties
1: 'text-[#03a9f4]',
0: 'text-[#ff3b30]',
3: 'text-[#ff3b30]',
4: 'text-[#ff3b30]',
};
export enum SubscribeFromEnum {
manual = 0,
subscribe= 1
manual = 0,
subscribe = 1
}
export const MatchPositionEnum = {
'header' : ('HTTP 请求头'),
'query': ('请求参数'),
'cookie' : ('Cookie')
header: 'HTTP 请求头',
query: '请求参数',
cookie: 'Cookie'
}
export const MatchTypeEnum = {
'EQUAL' : ('全等匹配'),
'PREFIX' : ('前缀匹配'),
'SUFFIX' :('后缀匹配'),
'SUBSTR' : ('子串匹配'),
'UNEQUAL' : ('非等匹配'),
'NULL' : ('空值匹配'),
'EXIST' : ('存在匹配'),
'UNEXIST':('不存在匹配'),
'REGEXP':('区分大小写的正则匹配'),
'REGEXPG':('不区分大小写的正则匹配'),
'unknown': ('任意匹配')
export const MatchTypeEnum = {
EQUAL: '全等匹配',
PREFIX: '前缀匹配',
SUFFIX: '后缀匹配',
SUBSTR: '子串匹配',
UNEQUAL: '非等匹配',
NULL: '空值匹配',
EXIST: '存在匹配',
UNEXIST: '不存在匹配',
REGEXP: '区分大小写的正则匹配',
REGEXPG: '不区分大小写的正则匹配',
unknown: '任意匹配'
}
export const SYSTEM_I18NEXT_FOR_ENUM = {
[SubscribeEnum.Rejected]:('驳回'),
[SubscribeEnum.Reviewing]:('审核中'),
[SubscribeEnum.Subscribed]:('已订阅'),
[SubscribeEnum.Unsubscribed]:('取消订阅'),
[SubscribeEnum.CancelRequest]:('取消申请'),
[SubscribeFromEnum.manual]:('手动添加'),
[SubscribeFromEnum.subscribe]:('订阅申请'),
[SubscribeEnum.Rejected]: '驳回',
[SubscribeEnum.Reviewing]: '审核中',
[SubscribeEnum.Subscribed]: '已订阅',
[SubscribeEnum.Unsubscribed]: '取消订阅',
[SubscribeEnum.CancelRequest]: '取消申请',
[SubscribeFromEnum.manual]: '手动添加',
[SubscribeFromEnum.subscribe]: '订阅申请'
}
export const HTTP_METHOD = ['GET','POST','PUT','DELETE','PATCH','HEAD']
export const HTTP_METHOD = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']
export const API_PROTOCOL = [
{label:'HTTP',value:'HTTP'},
{label:'HTTPS',value:'HTTPS'}
{ label: 'HTTP', value: 'HTTP' },
{ label: 'HTTPS', value: 'HTTPS' }
]
export const API_PATH_MATCH_RULES = [
{label:'前缀匹配',value:'prefix'},
{label:'全等匹配',value:'full'}
{ label: '前缀匹配', value: 'prefix' },
{ label: '全等匹配', value: 'full' }
]
export const ALGORITHM_ITEM = [
{label:'HS256',value:'HS256'},
{label:'HS384',value:'HS384'},
{label:'HS512',value:'HS512'},
{label:'RS256',value:'RS256'},
{label:'RS384',value:'RS384'},
{label:'RS512',value:'RS512'},
{label:'ES256',value:'ES256'},
{label:'ES384',value:'ES384'},
{label:'ES512',value:'ES512'},
{ label: 'HS256', value: 'HS256' },
{ label: 'HS384', value: 'HS384' },
{ label: 'HS512', value: 'HS512' },
{ label: 'RS256', value: 'RS256' },
{ label: 'RS384', value: 'RS384' },
{ label: 'RS512', value: 'RS512' },
{ label: 'ES256', value: 'ES256' },
{ label: 'ES384', value: 'ES384' },
{ label: 'ES512', value: 'ES512' }
]
export const SYSTEM_TABLE_COLUMNS: PageProColumns<SystemTableListItem>[] = [
{
title:('服务名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
fixed:'left',
sorter: (a,b)=> {
return a.name.localeCompare(b.name)
},
},
{
title:('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true,
},
{
title:('类型'),
dataIndex: 'service_kind',
width: 140,
ellipsis:true,
},
{
title:('所属团队'),
dataIndex: ['team','name'],
ellipsis:true,
},
{
title:('API 数量'),
dataIndex: 'apiNum',
ellipsis:true,
sorter: (a,b)=> {
return a.apiNum - b.apiNum
},
},
{
title: ('描述'),
dataIndex: 'description',
ellipsis:true,
},
{
title:('创建时间'),
dataIndex: 'createTime',
width:182,
ellipsis:true,
sorter: (a,b)=>frontendTimeSorter(a,b,'createTime')
{
title: '服务名称',
dataIndex: 'name',
ellipsis: true,
width: 160,
fixed: 'left',
sorter: (a, b) => {
return a.name.localeCompare(b.name)
}
];
export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: PageProColumns<SystemSubscriberTableListItem>[] = [
{
title:('服务名称'),
dataIndex: ['service','name'],
ellipsis:true,
width:160,
fixed:'left',
sorter: (a,b)=> {
return a.service.name.localeCompare(b.service.name)
},
},
{
title:('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title:('订阅方'),
dataIndex: ['subscriber','name'],
ellipsis:true
},
{
title:('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title:('来源'),
dataIndex: 'from',
ellipsis:true,
filters: true,
onFilter: true,
valueType: 'select',
},
{
title:('订阅时间'),
dataIndex: 'applyTime',
ellipsis:true,
width:182,
sorter: (a,b)=> {
return a.applyTime.localeCompare(b.applyTime)
},
},
];
export const SYSTEM_MEMBER_TABLE_COLUMN: PageProColumns<SystemMemberTableListItem>[] = [
{
title:('用户名'),
dataIndex: ['user','name'],
ellipsis:true,
width:160,
fixed:'left',
sorter: (a,b)=> {
return a.user.name.localeCompare(b.user.name)
},
},
{
title:('邮箱'),
dataIndex: 'email',
ellipsis:true
},
{
title:('角色'),
dataIndex: ['roles','name'],
ellipsis:true
}
];
export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
{
title:('参数位置'),
key: 'position',
renderText: (value:keyof typeof MatchPositionEnum) => {
return MatchPositionEnum[value]
},
required: true,
ellipsis:true
}, {
title:('参数名'),
key: 'key',
component: <Input className="w-INPUT_NORMAL"/>,
renderText: (value: unknown) => value,
required: true
}, {
title:('匹配类型'),
key: 'matchType',
renderText: (value:keyof typeof MatchTypeEnum) => {
return MatchTypeEnum[value]
},
required: true
}, {
title:('参数值'),
key: 'pattern',
unRender:(formValue)=>{return formValue?.matchType === 'NULL' || formValue?.matchType==='EXIST' || formValue?.matchType === 'UNEXIST'},
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
required: true
},
{
title: '服务 ID',
dataIndex: 'id',
width: 140,
ellipsis: true
},
{
title: '类型',
dataIndex: 'service_kind',
width: 140,
ellipsis: true
},
{
title: '所属团队',
dataIndex: ['team', 'name'],
ellipsis: true
},
{
title: 'API 数量',
dataIndex: 'apiNum',
ellipsis: true,
sorter: (a, b) => {
return a.apiNum - b.apiNum
}
},
{
title: '描述',
dataIndex: 'description',
ellipsis: true
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 182,
ellipsis: true,
sorter: (a, b) => frontendTimeSorter(a, b, 'createTime')
}
]
export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: PageProColumns<SystemSubscriberTableListItem>[] = [
{
title: '服务名称',
dataIndex: ['service', 'name'],
ellipsis: true,
width: 160,
fixed: 'left',
sorter: (a, b) => {
return a.service.name.localeCompare(b.service.name)
}
},
{
title: '服务 ID',
dataIndex: 'id',
width: 140,
ellipsis: true
},
{
title: '订阅方',
dataIndex: ['subscriber', 'name'],
ellipsis: true
},
{
title: '所属团队',
dataIndex: ['team', 'name'],
ellipsis: true
},
{
title: '来源',
dataIndex: 'from',
ellipsis: true,
filters: true,
onFilter: true,
valueType: 'select'
},
{
title: '订阅时间',
dataIndex: 'applyTime',
ellipsis: true,
width: 182,
sorter: (a, b) => {
return a.applyTime.localeCompare(b.applyTime)
}
}
]
export const SYSTEM_MEMBER_TABLE_COLUMN: PageProColumns<SystemMemberTableListItem>[] = [
{
title: '用户名',
dataIndex: ['user', 'name'],
ellipsis: true,
width: 160,
fixed: 'left',
sorter: (a, b) => {
return a.user.name.localeCompare(b.user.name)
}
},
{
title: '邮箱',
dataIndex: 'email',
ellipsis: true
},
{
title: '角色',
dataIndex: ['roles', 'name'],
ellipsis: true
}
]
export const MATCH_CONFIG: ConfigField<MatchItem>[] = [
{
title: '参数位置',
key: 'position',
renderText: (value: keyof typeof MatchPositionEnum) => {
return MatchPositionEnum[value]
},
required: true,
ellipsis: true
},
{
title: '参数名',
key: 'key',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: unknown) => value,
required: true
},
{
title: '匹配类型',
key: 'matchType',
renderText: (value: keyof typeof MatchTypeEnum) => {
return MatchTypeEnum[value]
},
required: true
},
{
title: '参数值',
key: 'pattern',
unRender: (formValue) => {
return formValue?.matchType === 'NULL' || formValue?.matchType === 'EXIST' || formValue?.matchType === 'UNEXIST'
},
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
required: true
}
]
export const SYSTEM_API_TABLE_COLUMNS: PageProColumns<SystemApiTableListItem>[] = [
{
title:('URL'),
dataIndex: 'requestPath',
ellipsis:true
},
{
title:('协议'),
dataIndex: 'protocols',
ellipsis:true,
renderText:(value)=>value?.join(', ')
},
{
title:('方法'),
dataIndex: 'methods',
ellipsis:true,
renderText:(value)=>value?.join(', ')
},
{
title:'是否放行',
dataIndex:'disable',
ellipsis:true,
filters: true,
onFilter: true,
valueType: 'select'
},
{
title:('描述'),
dataIndex: 'description',
ellipsis:true
},
{
title:('创建者'),
dataIndex: ['creator','name'],
ellipsis: true,
filters: true,
onFilter: true,
valueType: 'select',
filterSearch: true,
},
{
title:('更新时间'),
dataIndex: 'updateTime',
ellipsis:true,
hideInSearch: true,
width:182,
sorter: (a,b)=>frontendTimeSorter(a,b,'updateTime')
},
];
{
title: 'URL',
dataIndex: 'requestPath',
ellipsis: true
},
{
title: '协议',
dataIndex: 'protocols',
ellipsis: true,
renderText: (value) => value?.join(', ')
},
{
title: '方法',
dataIndex: 'methods',
ellipsis: true,
renderText: (value) => value?.join(', ')
},
{
title: '是否放行',
dataIndex: 'disable',
ellipsis: true,
filters: true,
onFilter: true,
valueType: 'select'
},
{
title: '描述',
dataIndex: 'description',
ellipsis: true
},
{
title: '创建者',
dataIndex: ['creator', 'name'],
ellipsis: true,
filters: true,
onFilter: true,
valueType: 'select',
filterSearch: true
},
{
title: '更新时间',
dataIndex: 'updateTime',
ellipsis: true,
hideInSearch: true,
width: 182,
sorter: (a, b) => frontendTimeSorter(a, b, 'updateTime')
}
]
export const UpstreamDriverEnum = {
'static':('静态上游'),
'discoveries':('动态服务发现'),
static: '静态上游',
discoveries: '动态服务发现'
}
export const UPSTREAM_TYPE_OPTIONS = [
{ label: ('静态上游'), value: 'static' },
// { label: ('动态服务发现', value: 'discoveries' },
];
{ label: '静态上游', value: 'static' }
// { label: ('动态服务发现', value: 'discoveries' },
]
export const schemeOptions = [
{ label:('HTTPS'), value:'HTTPS'},
{ label:('HTTP'), value:'HTTP'},
{ label: 'HTTPS', value: 'HTTPS' },
{ label: 'HTTP', value: 'HTTP' }
]
export const UPSTREAM_BALANCE_OPTIONS = [
{ label: ('带权轮询'), value: 'round-robin' },
{ label: ('IP Hash'), value: 'ip-hash' },
];
{ label: '带权轮询', value: 'round-robin' },
{ label: 'IP Hash', value: 'ip-hash' }
]
export const UPSTREAM_PASS_HOST_OPTIONS = [
{ label:('透传客户端请求 Host'), value:'pass'},
{ label:('使用上游服务 Host'), value:'node'},
{ label:('重写 Host'), value:'rewrite'},
{ label: '透传客户端请求 Host', value: 'pass' },
{ label: '使用上游服务 Host', value: 'node' },
{ label: '重写 Host', value: 'rewrite' }
]
export const UPSTREAM_PROXY_HEADER_TYPE_OPTIONS =[
{label:('新增或修改'), value: 'ADD' },
{ label: ('删除'), value: 'DELETE' }
export const UPSTREAM_PROXY_HEADER_TYPE_OPTIONS = [
{ label: '新增或修改', value: 'ADD' },
{ label: '删除', value: 'DELETE' }
]
export const PROXY_HEADER_CONFIG:ConfigField<ProxyHeaderItem>[] = [
{
title:('操作类型'),
key: 'optType',
renderText: (value: string) => {
return value === 'ADD' ? ('新增或修改'):('删除')
},
required: true
}, {
title:('参数名'),
key: 'key',
component: <Input className="w-INPUT_NORMAL"/>,
renderText: (value: string) => {
return value
},
required: true
}, {
title:('参数值'),
key: 'value',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
required: true
}
export const PROXY_HEADER_CONFIG: ConfigField<ProxyHeaderItem>[] = [
{
title: '操作类型',
key: 'optType',
renderText: (value: string) => {
return value === 'ADD' ? '新增或修改' : '删除'
},
required: true
},
{
title: '参数名',
key: 'key',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
required: true
},
{
title: '参数值',
key: 'value',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
required: true
}
]
export const SERVICE_VISUALIZATION_OPTIONS = [
{label:('内部服务:可通过网关访问,但不展示在服务广场'),value:'inner'},
{label:('公开服务:可通过网关访问,展示在服务广场,可被其他消费者订阅'),value:'public'}];
{ label: '内部服务:可通过网关访问,但不展示在服务广场', value: 'inner' },
{ label: '公开服务:可通过网关访问,展示在服务广场,可被其他消费者订阅', value: 'public' }
]
export const SERVICE_APPROVAL_OPTIONS = [
{label:('无需审核:允许任何消费者调用该服务'),value:'auto'},
{label:('人工审核:仅允许通过人工审核的消费者调用该服务'),value:'manual'}];
{ label: '无需审核:允许任何消费者调用该服务', value: 'auto' },
{ label: '人工审核:仅允许通过人工审核的消费者调用该服务', value: 'manual' }
]
export const SERVICE_KIND_OPTIONS = [
{label:('REST'),value:'rest'},
{label:('AI'),value:'ai'}];
{ label: 'REST', value: 'rest' },
{ label: 'AI', value: 'ai' }
]
export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: PageProColumns<GlobalNodeItem & {_id:string}>[] = [
{
title:('地址(IP 端口或域名)'),
dataIndex: 'address',
width: '50%',
formItemProps: {
className:'p-0 bg-transparent border-none',
rootClassName:'test',
rules: [
{
required: true,
whitespace: true
},
],
},
ellipsis:true
export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: PageProColumns<GlobalNodeItem & { _id: string }>[] = [
{
title: '地址(IP 端口或域名)',
dataIndex: 'address',
width: '50%',
formItemProps: {
className: 'p-0 bg-transparent border-none',
rootClassName: 'test',
rules: [
{
required: true,
whitespace: true
}
]
},
{
title:('权重(0-999'),
dataIndex: 'weight',
valueType:'digit',
formItemProps: {
className:'p-0 bg-transparent border-none'}
},
{
title: COLUMNS_TITLE.operate,
valueType: 'option',
btnNums:2,
render: ()=>null
},
];
export const SYSTEM_INSIDE_APPROVAL_TAB_ITEMS: TabsProps['items'] = [
{
key: '0',
label:('待审核'),
},
{
key: '1',
label: ('已审核'),
ellipsis: true
},
{
title: '权重(0-999',
dataIndex: 'weight',
valueType: 'digit',
formItemProps: {
className: 'p-0 bg-transparent border-none'
}
];
},
{
title: COLUMNS_TITLE.operate,
valueType: 'option',
btnNums: 2,
render: () => null
}
]
export const SYSTEM_INSIDE_APPROVAL_TAB_ITEMS: TabsProps['items'] = [
{
key: '0',
label: '待审核'
},
{
key: '1',
label: '已审核'
}
]
export const SYSTEM_PUBLISH_TAB_ITEMS: TabsProps['items'] = [
{
key: '0',
label: ('发布版本'),
},
{
key: '1',
label: ('发布申请记录'),
}
];
{
key: '0',
label: '发布版本'
},
{
key: '1',
label: '发布申请记录'
}
]
export const SYSTEM_SUBSCRIBE_APPROVAL_DETAIL_LIST = [
{
title:('服务名称'),key:'service',nested:'name'
},
{
title:('服务 ID'),key:'applyTeam',nested:'id'
},
{
title:('所属团队'),key:'team',nested:'name'
},
{
title:('所属系统'),key:'project',nested:'name'
},
{
title:('申请状态'),key:'status',renderText:()=>{}
},
{
title:('申请人'),key:'applier',nested:'name'
},
{
title:('申请时间'),key:'applyTime'
},
{
title: '服务名称',
key: 'service',
nested: 'name'
},
{
title: '服务 ID',
key: 'applyTeam',
nested: 'id'
},
{
title: '所属团队',
key: 'team',
nested: 'name'
},
{
title: '所属系统',
key: 'project',
nested: 'name'
},
{
title: '申请状态',
key: 'status',
renderText: () => {}
},
{
title: '申请人',
key: 'applier',
nested: 'name'
},
{
title: '申请时间',
key: 'applyTime'
}
]
export const SYSTEM_TOPOLOGY_NODE_TYPE_COLOR_MAP = {
subscriberProject:{
stroke:'#3291F8FF',
fill: '#3291F8FF',
name:('调用系统名称')
},
subscriberService:{
stroke:'#3D46F2',
fill: '#7371FC33',
name:('调用服务名称')
},
curProject:{
stroke:'#7371FCFF',
fill: '#7371FCFF',
name:('当前系统名称')
},
invokeService:{
stroke:'#3D46F2',
fill: '#7371FC33',
name:('被调用服务名称')
},
invokeProject:{
stroke:'#19C56BFF',
fill: '#19C56BFF',
name:('被调用系统名称')
},
application:{
stroke:'#ffa940',
fill: '#ffa94033',
subscriberProject: {
stroke: '#3291F8FF',
fill: '#3291F8FF',
name: '调用系统名称'
},
subscriberService: {
stroke: '#3D46F2',
fill: '#7371FC33',
name: '调用服务名称'
},
curProject: {
stroke: '#7371FCFF',
fill: '#7371FCFF',
name: '当前系统名称'
},
invokeService: {
stroke: '#3D46F2',
fill: '#7371FC33',
name: '被调用服务名称'
},
invokeProject: {
stroke: '#19C56BFF',
fill: '#19C56BFF',
name: '被调用系统名称'
},
application: {
stroke: '#ffa940',
fill: '#ffa94033'
}
}
export const SYSTEM_PUBLISH_ONLINE_COLUMNS = [
{
title: '上线结果',
dataIndex: 'status',
ellipsis: {
showTitle: true
}
};
export const SYSTEM_PUBLISH_ONLINE_COLUMNS = [
{
title:('上线结果'),
dataIndex: 'status',
ellipsis:{
showTitle:true
},
},
]
}
]
@@ -1,22 +1,24 @@
import { DeleteOutlined, EditOutlined } from '@ant-design/icons'
import { ActionType } from '@ant-design/pro-components'
import { ActionType, ProColumns } from '@ant-design/pro-components'
import InsidePage from '@common/components/aoplatform/InsidePage'
import PageList from '@common/components/aoplatform/PageList'
import PageList, { PageProColumns } from '@common/components/aoplatform/PageList'
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission'
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { Badge, Button, message, Select, Space, Tooltip } from 'antd'
import { Divider, message, Select, Space, Typography } from 'antd'
import React, { useEffect, useRef, useState } from 'react'
import ApiKeyModal from './components/ApiKeyModal'
import StatusFilter from './components/StatusFilter'
interface APIKey {
id: string
key: string
name: string
status: 'normal' | 'exceeded' | 'expired' | 'disabled' | 'error'
expirationDate: string
use_token: number
update_time: string
expire_time: string
can_delete: string
priority: number
isDefault: boolean
}
const KeySettings: React.FC = () => {
@@ -43,14 +45,6 @@ const KeySettings: React.FC = () => {
})
}, [])
const statusColors = {
normal: '#52c41a',
exceeded: '#ff4d4f',
expired: '#faad14',
disabled: '#d9d9d9',
error: '#ff4d4f'
}
const handleEdit = (record: APIKey) => {
setEditingKey(record)
setModalMode('edit')
@@ -109,63 +103,85 @@ const KeySettings: React.FC = () => {
)
}
const columns = [
const statusEnum = {
normal: { text: <Typography.Text type="success">{$t('正常')}</Typography.Text> },
exceeded: { text: <Typography.Text type="warning">{$t('超额')}</Typography.Text> },
expired: { text: <Typography.Text type="secondary">{$t('过期')}</Typography.Text> },
disabled: { text: <Typography.Text type="warning">{$t('停用')}</Typography.Text> },
error: { text: <Typography.Text type="danger">{$t('错误')}</Typography.Text> }
}
const operation: PageProColumns<APIKey>[] = [
{
title: $t('API Key'),
dataIndex: 'key',
render: (text: string, record: APIKey) => (
<Space>
{text}
{record.isDefault && <Badge count={$t('Default')} style={{ backgroundColor: statusColors.normal }} />}
</Space>
)
title: '',
key: 'option',
btnNums: 3,
fixed: 'right',
valueType: 'option',
render: (_: React.ReactNode, entity: APIKey) => [
<TableBtnWithPermission
access="system.settings.ai_key_resource.manager"
key="edit"
btnType="edit"
onClick={() => handleEdit(entity)}
btnTitle={$t('编辑')}
/>,
<Divider type="vertical" className="mx-0" key="div3" />,
<TableBtnWithPermission
access="system.settings.ai_key_resource.manager"
key="delete"
btnType="delete"
onClick={() => handleDelete(entity.id)}
btnTitle={$t('删除')}
/>
// <Tooltip title={$t('编辑')}>
// <Button type="text" icon={<EditOutlined />} onClick={() => handleEdit(entity)} />
// </Tooltip>,
// entity.can_delete && (
// <Tooltip title={$t('删除')}>
// <Button type="text" icon={<DeleteOutlined />} danger onClick={() => handleDelete(entity.id)} />
// </Tooltip>
// )
]
}
]
const columns: ProColumns<APIKey>[] = [
{
title: $t('调用优先级'),
dataIndex: 'priority',
width: '100px'
},
{
title: $t('名称'),
dataIndex: 'name',
render: (text: string, record: APIKey) => <Space>{text}</Space>
},
{
title: $t('状态'),
dataIndex: 'status',
render: (status: keyof typeof statusColors) => (
<Badge
status="processing"
text={status.charAt(0).toUpperCase() + status.slice(1)}
style={{ color: statusColors[status] }}
/>
)
ellipsis: true,
valueType: 'select',
filters: true,
onFilter: true,
valueEnum: statusEnum,
render: (status: APIKey['status']) => statusEnum[status]?.text || status
},
{
title: $t('已用 Token'),
dataIndex: 'use_token',
render: (value: number) => value.toLocaleString()
},
{
title: $t('编辑时间'),
dataIndex: 'update_time'
},
{
title: $t('过期时间'),
dataIndex: 'expirationDate'
dataIndex: 'expire_time',
render: (expireTime: string) => (expireTime === '0' ? $t('永不过期') : expireTime)
},
{
title: $t('优先级'),
dataIndex: 'priority'
},
{
title: $t('操作'),
key: 'actions',
render: (_: unknown, record: APIKey) => (
<Space>
<Tooltip title={$t('Edit')}>
<Button
type="text"
icon={<EditOutlined />}
disabled={record.isDefault}
onClick={() => handleEdit(record)}
/>
</Tooltip>
<Tooltip title={$t('Delete')}>
<Button
type="text"
icon={<DeleteOutlined />}
disabled={record.isDefault}
danger
onClick={() => handleDelete(record.id)}
/>
</Tooltip>
</Space>
),
width: 120,
btnNums: 2
}
...operation
]
const beforeSearchNode = [
@@ -184,9 +200,9 @@ const KeySettings: React.FC = () => {
return (
<InsidePage
className="overflow-y-auto pb-PAGE_INSIDE_B"
className="overflow-y-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X"
pageTitle={$t('APIKey 资源池')}
description={$t('支持单个 API 模型供应商下创建多个 APIKey,并可对多个 APIKEY 进行智能负载均衡')}
description={$t('支持单个 API 模型供应商下创建多个 APIKey,并可对多个 APIkey 进行智能负载均衡')}
showBorder={false}
scrollPage={false}
>
@@ -1,221 +1,224 @@
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList.tsx"
import {ActionType} from "@ant-design/pro-components";
import {FC, useEffect, useMemo, useRef, useState} from "react";
import {Link, useNavigate, useParams} from "react-router-dom";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {App, Divider} from "antd";
import {BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import { SimpleMemberItem} from '@common/const/type.ts'
import {useFetch} from "@common/hooks/http.ts";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { SYSTEM_API_TABLE_COLUMNS } from "../../../const/system/const.tsx";
import {SystemApiTableListItem } from "../../../const/system/type.ts";
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission.tsx";
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { checkAccess } from "@common/utils/permission.ts";
import { $t } from "@common/locales/index.ts";
import { ActionType } from '@ant-design/pro-components'
import PageList, { PageProColumns } from '@common/components/aoplatform/PageList.tsx'
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission.tsx'
import { BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const.tsx'
import { SimpleMemberItem } from '@common/const/type.ts'
import { useBreadcrumb } from '@common/contexts/BreadcrumbContext.tsx'
import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx'
import { useFetch } from '@common/hooks/http.ts'
import { $t } from '@common/locales/index.ts'
import { checkAccess } from '@common/utils/permission.ts'
import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx'
import { App, Divider, Typography } from 'antd'
import { FC, useEffect, useMemo, useRef, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { SYSTEM_API_TABLE_COLUMNS } from '../../../const/system/const.tsx'
import { SystemApiTableListItem } from '../../../const/system/type.ts'
const SystemInsideRouterList:FC = ()=>{
const [searchWord, setSearchWord] = useState<string>('')
const { setBreadcrumb } = useBreadcrumb()
const { modal,message } = App.useApp()
const [tableListDataSource, setTableListDataSource] = useState<SystemApiTableListItem[]>([]);
const [tableHttpReload, setTableHttpReload] = useState(true);
const {fetchData} = useFetch()
const pageListRef = useRef<ActionType>(null);
const [memberValueEnum, setMemberValueEnum] = useState<SimpleMemberItem[]>([])
const {accessData,state} = useGlobalContext()
const {serviceId, teamId} = useParams<RouterParams>()
const navigator = useNavigate()
const SystemInsideRouterList: FC = () => {
const [searchWord, setSearchWord] = useState<string>('')
const { setBreadcrumb } = useBreadcrumb()
const { modal, message } = App.useApp()
const [tableListDataSource, setTableListDataSource] = useState<SystemApiTableListItem[]>([])
const [tableHttpReload, setTableHttpReload] = useState(true)
const { fetchData } = useFetch()
const pageListRef = useRef<ActionType>(null)
const [memberValueEnum, setMemberValueEnum] = useState<SimpleMemberItem[]>([])
const { accessData, state } = useGlobalContext()
const { serviceId, teamId } = useParams<RouterParams>()
const navigator = useNavigate()
const getRoutesList = (): Promise<{ data: SystemApiTableListItem[], success: boolean }>=> {
if(!tableHttpReload){
setTableHttpReload(true)
return Promise.resolve({
data: tableListDataSource,
success: true,
});
}
return fetchData<BasicResponse<{routers:SystemApiTableListItem}>>('service/routers',{method:'GET',eoParams:{service:serviceId,team:teamId, keyword:searchWord},eoTransformKeys:['request_path','create_time','update_time','disable']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTableListDataSource(data.routers)
setTableHttpReload(false)
return {data:data.routers, success: true}
}else{
message.error(msg || $t(RESPONSE_TIPS.error))
return {data:[], success:false}
}
}).catch(() => {
return {data:[], success:false}
})
const getRoutesList = (): Promise<{ data: SystemApiTableListItem[]; success: boolean }> => {
if (!tableHttpReload) {
setTableHttpReload(true)
return Promise.resolve({
data: tableListDataSource,
success: true
})
}
const deleteRoute = (entity:SystemApiTableListItem)=>{
return new Promise((resolve, reject)=>{
fetchData<BasicResponse<null>>('service/router',{method:'DELETE',eoParams:{service:serviceId,team:teamId, router:entity!.id}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || $t(RESPONSE_TIPS.success))
resolve(true)
}else{
message.error(msg || $t(RESPONSE_TIPS.error))
reject(msg || $t(RESPONSE_TIPS.error))
}
}).catch((errorInfo)=> reject(errorInfo))
})
}
const openModal = async (type: 'delete',entity:SystemApiTableListItem) =>{
let title:string = ''
let content:string|React.ReactNode = ''
switch (type){
case 'delete':
title=$t('删除')
content=$t(DELETE_TIPS.default)
break;
return fetchData<BasicResponse<{ routers: SystemApiTableListItem }>>('service/routers', {
method: 'GET',
eoParams: { service: serviceId, team: teamId, keyword: searchWord },
eoTransformKeys: ['request_path', 'create_time', 'update_time', 'disable']
})
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setTableListDataSource(data.routers)
setTableHttpReload(false)
return { data: data.routers, success: true }
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
return { data: [], success: false }
}
})
.catch(() => {
return { data: [], success: false }
})
}
modal.confirm({
title,
content,
onOk:()=> {
switch (type){
case 'delete':
return deleteRoute(entity).then((res)=>{if(res === true) manualReloadTable()})
}
},
width:600,
okText:$t('确认'),
okButtonProps:{
disabled : !checkAccess( `team.service.router.${type}`, accessData )
},
cancelText:$t('取消'),
closable:true,
icon:<></>,
})
}
const operation:PageProColumns<SystemApiTableListItem>[] =[
{
title: COLUMNS_TITLE.operate,
key: 'option',
btnNums:2,
fixed:'right',
valueType: 'option',
render: (_: React.ReactNode, entity: SystemApiTableListItem) => [
<TableBtnWithPermission access="team.service.router.edit" key="edit" btnType="edit" onClick={()=>{navigator(`/service/${teamId}/inside/${serviceId}/route/${entity.id}`)}} btnTitle="编辑"/>,
<Divider type="vertical" className="mx-0" key="div3"/>,
<TableBtnWithPermission access="team.service.router.delete" key="delete" btnType="delete" onClick={()=>{openModal('delete',entity)}} btnTitle="删除"/>,
],
}
]
const manualReloadTable = () => {
setTableHttpReload(true); // 表格数据需要从后端接口获取
pageListRef.current?.reload()
};
const getMemberList = async ()=>{
setMemberValueEnum([])
const {code,data,msg} = await fetchData<BasicResponse<{ members: SimpleMemberItem[] }>>('simple/member',{method:'GET'})
if(code === STATUS_CODE.SUCCESS){
setMemberValueEnum(data.members)
}else{
const deleteRoute = (entity: SystemApiTableListItem) => {
return new Promise((resolve, reject) => {
fetchData<BasicResponse<null>>('service/router', {
method: 'DELETE',
eoParams: { service: serviceId, team: teamId, router: entity!.id }
})
.then((response) => {
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || $t(RESPONSE_TIPS.success))
resolve(true)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
reject(msg || $t(RESPONSE_TIPS.error))
}
})
.catch((errorInfo) => reject(errorInfo))
})
}
const openModal = async (type: 'delete', entity: SystemApiTableListItem) => {
let title: string = ''
let content: string | React.ReactNode = ''
switch (type) {
case 'delete':
title = $t('删除')
content = $t(DELETE_TIPS.default)
break
}
// const openDrawer = (type:'add'|'edit'|'view',entity?:SystemApiTableListItem)=>{
// setCurApi(entity)
// setDrawerType(type)
// }
modal.confirm({
title,
content,
onOk: () => {
switch (type) {
case 'delete':
return deleteRoute(entity).then((res) => {
if (res === true) manualReloadTable()
})
}
},
width: 600,
okText: $t('确认'),
okButtonProps: {
disabled: !checkAccess(`team.service.router.${type}`, accessData)
},
cancelText: $t('取消'),
closable: true,
icon: <></>
})
}
// useEffect(()=>{drawerType !== undefined ? setOpen(true):setOpen(false)},[drawerType])
const operation: PageProColumns<SystemApiTableListItem>[] = [
{
title: COLUMNS_TITLE.operate,
key: 'option',
btnNums: 2,
fixed: 'right',
valueType: 'option',
render: (_: React.ReactNode, entity: SystemApiTableListItem) => [
<TableBtnWithPermission
access="team.service.router.edit"
key="edit"
btnType="edit"
onClick={() => {
navigator(`/service/${teamId}/inside/${serviceId}/route/${entity.id}`)
}}
btnTitle="编辑"
/>,
<Divider type="vertical" className="mx-0" key="div3" />,
<TableBtnWithPermission
access="team.service.router.delete"
key="delete"
btnType="delete"
onClick={() => {
openModal('delete', entity)
}}
btnTitle="删除"
/>
]
}
]
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}>{$t('服务')}</Link>
},
{
title:$t('路由')
}
])
getMemberList()
manualReloadTable()
}, [serviceId]);
const manualReloadTable = () => {
setTableHttpReload(true) // 表格数据需要从后端接口获取
pageListRef.current?.reload()
}
// const onClose = () => {
// setDrawerType(undefined);
// setCurApi(undefined)
// };
const columns = useMemo(()=>{
return [...SYSTEM_API_TABLE_COLUMNS].map(x=>{
if(x.filters &&((x.dataIndex as string[])?.indexOf('creator') !== -1) ){
const tmpValueEnum:{[k:string]:{text:string}} = {}
memberValueEnum?.forEach((x:SimpleMemberItem)=>{
tmpValueEnum[x.name] = {text:x.name}
})
x.valueEnum = tmpValueEnum
}
if(x.filters &&((x.dataIndex as string[])?.indexOf('disable') !== -1) ){
x.valueEnum = {
true:{text:<span className="text-status_fail">{$t('拦截')}</span>},
false:{text:<span className="text-status_success">{$t('放行')}</span>}
}
}
return {...x,title:typeof x.title === 'string' ? $t(x.title as string) : x.title}})
},[memberValueEnum,state.language])
const getMemberList = async () => {
setMemberValueEnum([])
const { code, data, msg } = await fetchData<BasicResponse<{ members: SimpleMemberItem[] }>>('simple/member', {
method: 'GET'
})
if (code === STATUS_CODE.SUCCESS) {
setMemberValueEnum(data.members)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
}
// const handlerSubmit:() => Promise<string | boolean>|undefined= ()=>{
// switch(drawerType){
// case 'add':{
// return drawerAddFormRef.current?.save()?.then((res)=>{res && manualReloadTable();return res})
// }
// case 'edit':{
// return drawerAddFormRef.current?.save()?.then((res)=>{res && manualReloadTable();return res})
// }
// default:return undefined
// }
// }
useEffect(() => {
setBreadcrumb([
{
title: <Link to={`/service/list`}>{$t('服务')}</Link>
},
{
title: $t('路由')
}
])
getMemberList()
manualReloadTable()
}, [serviceId])
return (
<>
<PageList
id="global_system_api"
ref={pageListRef}
columns = {[...columns,...operation]}
request={()=>getRoutesList()}
dataSource={tableListDataSource}
addNewBtnTitle={$t('添加路由')}
searchPlaceholder={$t('输入 URL 查找路由')}
// onAddNewBtnClick={()=>{openDrawer('add')}}
onAddNewBtnClick={()=>{navigator(`/service/${teamId}/inside/${serviceId}/route/create`)}}
addNewBtnAccess="team.service.router.add"
tableClickAccess="team.service.router.view"
manualReloadTable={manualReloadTable}
onSearchWordChange={(e)=>{setSearchWord(e.target.value)}}
onChange={() => {
setTableHttpReload(false)
}}
onRowClick={(row:SystemApiTableListItem)=>navigator(`/service/${teamId}/inside/${serviceId}/route/${row.id}`)}
tableClass="mr-PAGE_INSIDE_X "
/>
{/* <DrawerWithFooter
title={drawerType === 'add' ? $t("添加路由"):$t("路由详情")}
open={open}
onClose={onClose}
onSubmit={()=>handlerSubmit()}
showOkBtn={drawerType !== 'view'}
>
<SystemInsideRouterCreate ref={drawerAddFormRef} type={drawerType as 'add'|'edit'|'copy'} entity={drawerType === 'edit' ? curApi : undefined} modalApiPrefix={apiPrefix} serviceId={serviceId!} teamId={teamId!} modalPrefixForce={prefixForce}/>
</DrawerWithFooter> */}
</>
)
const columns = useMemo(() => {
return [...SYSTEM_API_TABLE_COLUMNS].map((x) => {
if (x.filters && (x.dataIndex as string[])?.indexOf('creator') !== -1) {
const tmpValueEnum: { [k: string]: { text: string } } = {}
memberValueEnum?.forEach((x: SimpleMemberItem) => {
tmpValueEnum[x.name] = { text: x.name }
})
x.valueEnum = tmpValueEnum
}
if (x.filters && (x.dataIndex as string[])?.indexOf('disable') !== -1) {
x.valueEnum = {
true: { text: <Typography.Text type="danger">{$t('拦截')}</Typography.Text> },
false: { text: <Typography.Text type="success">{$t('放行')}</Typography.Text> }
}
}
return { ...x, title: typeof x.title === 'string' ? $t(x.title as string) : x.title }
})
}, [memberValueEnum, state.language])
return (
<>
<PageList
id="global_system_api"
ref={pageListRef}
columns={[...columns, ...operation]}
request={() => getRoutesList()}
dataSource={tableListDataSource}
addNewBtnTitle={$t('添加路由')}
searchPlaceholder={$t('输入 URL 查找路由')}
onAddNewBtnClick={() => {
navigator(`/service/${teamId}/inside/${serviceId}/route/create`)
}}
addNewBtnAccess="team.service.router.add"
tableClickAccess="team.service.router.view"
manualReloadTable={manualReloadTable}
onSearchWordChange={(e) => {
setSearchWord(e.target.value)
}}
onChange={() => {
setTableHttpReload(false)
}}
onRowClick={(row: SystemApiTableListItem) =>
navigator(`/service/${teamId}/inside/${serviceId}/route/${row.id}`)
}
tableClass="mr-PAGE_INSIDE_X "
/>
</>
)
}
export default SystemInsideRouterList
export default SystemInsideRouterList