Compare commits

...

24 Commits

Author SHA1 Message Date
ningyv b7bb409e96 fix: consumer permission 2024-12-13 20:00:38 +08:00
lichunxian 9fa43ccc00 Merge branch 'fix/certificatePermission' into 'main'
fix: table-permission

See merge request apipark/APIPark!124
2024-12-10 15:17:11 +08:00
ningyv c2a11050dd fix: table-permission 2024-12-10 15:16:19 +08:00
lichunxian ccc39b95de Merge branch 'fix/certificatePermission' into 'main'
fix: implement certificate popup permission handling

See merge request apipark/APIPark!123
2024-12-09 15:22:29 +08:00
ningyv 9a2782e54b fix: implement certificate popup permission handling 2024-12-09 15:18:59 +08:00
lichunxian baf8ed4830 Merge branch 'feature/dataLogPage' into 'main'
fix: improve column width adjustment and optimize date picker performance

See merge request apipark/APIPark!122
2024-12-06 18:03:16 +08:00
ningyv dedb586daf fix: improve column width adjustment and optimize date picker performance 2024-12-06 18:02:44 +08:00
刘健 21cd823791 Merge branch 'feature/data-mask' into 'main'
update publish problem

See merge request apipark/APIPark!121
2024-12-06 16:13:23 +08:00
lichunxian f1c16fd992 Merge branch 'feature/dataLogPage' into 'main'
Feature/data log page

See merge request apipark/APIPark!120
2024-12-06 15:25:51 +08:00
ningyv 52035341f6 fix: refine time range calculation with second-level precision 2024-12-06 15:24:06 +08:00
ningyv aa62d44717 fix: resolve subscriber permissions 2024-12-06 14:22:59 +08:00
lichunxian a072d1fc8d Merge branch 'feature/dataLogPage' into 'main'
feat: integrate global policy API and implement data log page

See merge request apipark/APIPark!119
2024-12-06 11:51:13 +08:00
ningyv 38a00570d0 feat: integrate global policy API and implement data log page 2024-12-06 11:50:32 +08:00
刘健 43283b9da3 Merge branch 'feature/data-mask' into 'main'
update service publish

See merge request apipark/APIPark!118
2024-12-06 11:32:15 +08:00
刘健 3eb4f98fd8 Merge branch 'feature/data-mask' into 'main'
add service strategy log

See merge request apipark/APIPark!117
2024-12-06 10:49:21 +08:00
刘健 b8308a446b Merge branch 'feature/data-mask' into 'main'
update service publish

See merge request apipark/APIPark!116
2024-12-06 10:40:20 +08:00
lichunxian 952c519e45 Merge branch 'feature/dataLogPage' into 'main'
feat: integrate global policy API and implement data log page

See merge request apipark/APIPark!115
2024-12-05 18:53:51 +08:00
ningyv 07d97fa0bf feat: integrate global policy API and implement data log page 2024-12-05 18:53:11 +08:00
刘健 b0defedf04 Merge branch 'feature/data-mask' into 'main'
fix log bug:Keyword query failed

See merge request apipark/APIPark!114
2024-12-05 18:17:49 +08:00
刘健 a75b8a3f13 Merge branch 'feature/data-mask' into 'main'
fix logs bug

See merge request apipark/APIPark!113
2024-12-05 17:21:46 +08:00
刘健 9ab7989c8b Merge branch 'feature/data-mask' into 'main'
fix log bug

See merge request apipark/APIPark!112
2024-12-05 17:15:04 +08:00
刘健 4bae2edc49 Merge branch 'feature/data-mask' into 'main'
update strategy publish bug

See merge request apipark/APIPark!111
2024-12-05 15:44:22 +08:00
刘健 e01f596525 Merge branch 'feature/data-mask' into 'main'
update log label

See merge request apipark/APIPark!109
2024-12-05 15:06:28 +08:00
刘健 570c80af91 Merge branch 'feature/data-mask' into 'main'
Feature/data mask

See merge request apipark/APIPark!108
2024-12-05 14:50:19 +08:00
25 changed files with 653 additions and 499 deletions
@@ -146,8 +146,11 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
...x, ...x,
title: typeof x.title === 'string' ? $t(x.title) : x.title, title: typeof x.title === 'string' ? $t(x.title) : x.title,
...(x.dataIndex === 'status' ? { ...(x.dataIndex === 'status' ? {
render:(_,entity)=>( render:(_,entity)=> (
entity.status === 0 ? $t('正常') : $t('无效')) <span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>
{$t(ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-')}
</span>
)
}:{}) }:{})
} }
}),[state.language]) }),[state.language])
@@ -37,6 +37,8 @@ const TableIconName = {
const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateTo, onClick, className, btnType }: TableBtnWithPermissionProps) => { const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateTo, onClick, className, btnType }: TableBtnWithPermissionProps) => {
const [btnAccess, setBtnAccess] = useState<boolean>(false) const [btnAccess, setBtnAccess] = useState<boolean>(false)
const [btnStatus, setBtnStatus] = useState<boolean>(false)
const [closeToolTip, setCloseToolTip] = useState<boolean>(false)
const { accessData, checkPermission, accessInit } = useGlobalContext() const { accessData, checkPermission, accessInit } = useGlobalContext()
const navigate = useNavigate() const navigate = useNavigate()
const lastAccess = useMemo(() => { const lastAccess = useMemo(() => {
@@ -52,16 +54,27 @@ const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateT
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => { const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation() e.stopPropagation()
setTimeout(() => {
setBtnStatus(false)
setCloseToolTip(true)
})
navigateTo ? navigate(navigateTo) : onClick?.() navigateTo ? navigate(navigateTo) : onClick?.()
}, [navigateTo, navigate, onClick]) }, [navigateTo, navigate, onClick])
const changeTooltipStatus = (open: boolean) => {
setBtnStatus(open)
if (closeToolTip) {
setBtnStatus(false)
setCloseToolTip(false)
}
}
return (<>{ return (<>{
!btnAccess || (disabled && tooltip) ? !btnAccess || (disabled && tooltip) ?
<Tooltip placement="top" title={tooltip ?? $t('暂无(0)权限,请联系管理员分配。', [$t(btnTitle).toLowerCase()])}> <Tooltip placement="top" title={tooltip ?? $t('暂无(0)权限,请联系管理员分配。', [$t(btnTitle).toLowerCase()])}>
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} >{ }</Button> <Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} >{ }</Button>
</Tooltip> </Tooltip>
: :
<Tooltip placement="top" title={$t(btnTitle)}> <Tooltip placement="top" title={$t(btnTitle)} trigger='hover' open={btnStatus} onOpenChange={changeTooltipStatus}>
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} onClick={handleClick}>{ }</Button> <Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} onClick={handleClick}>{ }</Button>
</Tooltip> </Tooltip>
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd'; import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd';
import dayjs, { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat'; import customParseFormat from 'dayjs/plugin/customParseFormat';
@@ -12,69 +12,80 @@ export type RangeValue = [Dayjs | null, Dayjs | null] | null;
dayjs.extend(customParseFormat); dayjs.extend(customParseFormat);
export type TimeRange = { export type TimeRange = {
start:number|null start: number | null
end:number|null end: number | null
} }
export type TimeRangeButton = ''| 'hour' | 'day' | 'threeDays' | 'sevenDays'; export type TimeRangeButton = '' | 'hour' | 'day' | 'threeDays' | 'sevenDays';
type TimeRangeSelectorProps = { type TimeRangeSelectorProps = {
initialTimeButton?:TimeRangeButton, initialTimeButton?: TimeRangeButton,
initialDatePickerValue?:RangeValue initialDatePickerValue?: RangeValue
onTimeRangeChange?:(timeRange:TimeRange) =>void onTimeRangeChange?: (timeRange: TimeRange) => void
hideTitle?:boolean hideTitle?: boolean
onTimeButtonChange:(time:TimeRangeButton) =>void onTimeButtonChange: (time: TimeRangeButton) => void
labelSize?:'small'|'default' labelSize?: 'small' | 'default'
} bindRef?: any
const TimeRangeSelector = (props:TimeRangeSelectorProps) => { hideBtns?: TimeRangeButton[]
const {initialTimeButton,initialDatePickerValue,onTimeRangeChange,hideTitle,onTimeButtonChange,labelSize='default'} = props defaultTimeButton?: TimeRangeButton
}
const TimeRangeSelector = (props: TimeRangeSelectorProps) => {
const { initialTimeButton, initialDatePickerValue, onTimeRangeChange, hideTitle, onTimeButtonChange, labelSize = 'default', bindRef, hideBtns = [], defaultTimeButton = 'hour' } = props
const [timeButton, setTimeButton] = useState(initialTimeButton || ''); const [timeButton, setTimeButton] = useState(initialTimeButton || '');
const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null,null]); const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null, null]);
useEffect(() => {
if (bindRef) {
bindRef({ reset });
}
}, [bindRef])
// 根据选择的时间范围计算开始和结束时间 // 根据选择的时间范围计算开始和结束时间
const calculateTimeRange = (curBtn:'hour'|'day'|'threeDays'|'sevenDays') => { const calculateTimeRange = (curBtn: TimeRangeButton) => {
const currentSecond = new Date().getTime() // 当前毫秒数时间戳 const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
const currentMin = currentSecond - (currentSecond % (60 * 1000)) // 当前分钟数时间戳 let startMin = currentSecond - 60 * 60
let startMin = currentMin - 60 * 60 * 1000
switch (curBtn) { switch (curBtn) {
case 'hour': { case 'hour': {
startMin = currentMin - 60 * 60 * 1000 startMin = currentSecond - 60 * 60
break break
}
case 'day': {
startMin = currentMin - 24 * 60 * 60 * 1000
break
}
case 'threeDays': {
startMin =
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
2 * 24 * 60 * 60 * 1000
break
}
case 'sevenDays': {
startMin =
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
6 * 24 * 60 * 60 * 1000
break
}
} }
case 'day': {
startMin = currentSecond - 24 * 60 * 60
break
}
case 'threeDays': {
startMin =
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
2 * 24 * 60 * 60
break
}
case 'sevenDays': {
startMin =
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
6 * 24 * 60 * 60
break
}
}
if (onTimeRangeChange) { if (onTimeRangeChange) {
onTimeRangeChange({ start: startMin / 1000, end: currentMin / 1000 }); onTimeRangeChange({ start: startMin, end: currentSecond });
} }
}; };
// 处理单选按钮的变化 // 处理单选按钮的变化
const handleRadioChange = (e:RadioChangeEvent) => { const handleRadioChange = (e: RadioChangeEvent) => {
setTimeButton(e.target.value); setTimeButton(e.target.value);
onTimeButtonChange?.(e.target.value) onTimeButtonChange?.(e.target.value)
setDatePickerValue(null) setDatePickerValue(null)
calculateTimeRange(e.target.value); calculateTimeRange(e.target.value);
}; };
const reset = () => {
setTimeButton(defaultTimeButton)
calculateTimeRange(defaultTimeButton)
setDatePickerValue(null)
}
// 处理日期选择器的变化 // 处理日期选择器的变化
const handleDatePickerChange = (dates: RangeValue) => { const handleDatePickerChange = (dates: RangeValue) => {
setTimeButton(dates ? '' : 'hour') setTimeButton(dates ? '' : defaultTimeButton)
onTimeButtonChange?.(dates ? '' : 'hour') onTimeButtonChange?.(dates ? '' : defaultTimeButton)
setDatePickerValue(dates); setDatePickerValue(dates);
if (dates && Array.isArray(dates) && dates.length === 2) { if (dates && Array.isArray(dates) && dates.length === 2) {
const [startDate, endDate] = dates; const [startDate, endDate] = dates;
@@ -84,34 +95,37 @@ const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
onTimeRangeChange({ start, end }); onTimeRangeChange({ start, end });
} }
} }
if (!dates) {
calculateTimeRange(defaultTimeButton)
}
}; };
const disabledDate: RangePickerProps['disabledDate'] = (current) => { const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today // Can not select days before today and today
return current && current.valueOf() > dayjs().startOf('day').valueOf(); return current && current.valueOf() > dayjs().startOf('day').valueOf();
}; };
return ( return (
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase"> <div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>} {!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid"> <Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
<Radio.Button value="hour">{$t('近1小时')}</Radio.Button> {hideBtns?.length && hideBtns.includes('hour') ? null : <Radio.Button value="hour">{$t('近1小时')}</Radio.Button>}
<Radio.Button value="day">{$t('近24小时')}</Radio.Button> {hideBtns?.length && hideBtns.includes('day') ? null : <Radio.Button value="day">{$t('近24小时')}</Radio.Button>}
<Radio.Button value="threeDays">{$t('近3天')}</Radio.Button> {hideBtns?.length && hideBtns.includes('threeDays') ? null : <Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>}
<Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button> {hideBtns?.length && hideBtns.includes('sevenDays') ? null : <Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>}
</Radio.Group> </Radio.Group>
<DatePicker.RangePicker <DatePicker.RangePicker
value={datePickerValue} value={datePickerValue}
className="rounded-s-none ml-[-1px]" className="rounded-s-none ml-[-1px]"
disabledDate={disabledDate} disabledDate={disabledDate}
onChange={handleDatePickerChange} onChange={handleDatePickerChange}
onOpenChange={(open)=>{ onOpenChange={(open) => {
if(!open && datePickerValue && datePickerValue.length > 2){ if (!open && datePickerValue && datePickerValue.length > 2) {
setTimeButton('') setTimeButton('')
onTimeButtonChange?.('') onTimeButtonChange?.('')
} }
}} }}
/> />
</div> </div>
@@ -181,6 +181,10 @@ export const TranslateWord = ()=>{
{$t('请输入IP地址或CIDR范围,每条以换行分割')} {$t('请输入IP地址或CIDR范围,每条以换行分割')}
{$t('待更新')} {$t('待更新')}
{$t('待删除')} {$t('待删除')}
{$t('内容')}
{$t('调用地址')}
{$t('消费者 IP')}
{$t('鉴权名称')}
</> </>
) )
} }
@@ -13,6 +13,7 @@ export interface CodeboxApiRef {
formatCode: () => void formatCode: () => void
} }
export type codeBoxLanguagesType = 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
interface CodeboxProps { interface CodeboxProps {
options?: MonacoEditor.IStandaloneEditorConstructionOptions options?: MonacoEditor.IStandaloneEditorConstructionOptions
value?: string value?: string
@@ -22,7 +23,7 @@ interface CodeboxProps {
height?: string | null height?: string | null
readOnly?: boolean readOnly?: boolean
apiRef?: RefObject<CodeboxApiRef> apiRef?: RefObject<CodeboxApiRef>
language?: 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml' language?: codeBoxLanguagesType
extraContent?:React.ReactNode extraContent?:React.ReactNode
sx?:Record<string,unknown> sx?:Record<string,unknown>
editorTheme?:'vs' | 'vs-dark' | 'hc-black' editorTheme?:'vs' | 'vs-dark' | 'hc-black'
@@ -486,17 +486,17 @@ export const PERMISSION_DEFINITION = [
}, },
"team.application.subscription.add": { "team.application.subscription.add": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.subscription.subscribe"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.subscribe"] }]
} }
}, },
"team.application.subscription.edit": { "team.application.subscription.edit": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.subscription.manager_subscribed_services"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.manager_subscribed_services"] }]
} }
}, },
"team.application.subscription.delete": { "team.application.subscription.delete": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.team.consumer.subscription.manager_subscribed_services"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.team.consumer.subscription.manager_subscribed_services"] }]
} }
}, },
"team.application.application.view": { "team.application.application.view": {
@@ -506,47 +506,47 @@ export const PERMISSION_DEFINITION = [
}, },
"team.application.application.add": { "team.application.application.add": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
} }
}, },
"team.application.application.edit": { "team.application.application.edit": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
} }
}, },
"team.application.application.delete": { "team.application.application.delete": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
} }
}, },
"team.consumer.authorization.view": { "team.consumer.authorization.view": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","system.workspace.application.view_all","team.consumer.authorization.view"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","system.workspace.application.view_all","team.consumer.authorization.view"] }]
} }
}, },
"team.application.authorization.add": { "team.application.authorization.add": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
} }
}, },
"team.application.authorization.edit": { "team.application.authorization.edit": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
} }
}, },
"team.application.authorization.delete": { "team.application.authorization.delete": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
} }
}, },
"team.application.authorization.cancelSubApply": { "team.application.authorization.cancelSubApply": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
} }
}, },
"team.application.authorization.cancelSub": { "team.application.authorization.cancelSub": {
"granted": { "granted": {
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }] "anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
} }
}, },
"team.team.team.view": { "team.team.team.view": {
@@ -1,3 +1,4 @@
import { codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
export const MatchRules = [ export const MatchRules = [
{ value: 'inner', label: '数据格式' }, { value: 'inner', label: '数据格式' },
@@ -54,4 +55,34 @@ export const StrategyStatusEnum = {
"offline":'text-status_fail', "offline":'text-status_fail',
"delete":'text-status_offline', "delete":'text-status_offline',
} }
export const contentTypeToLanguageMap: Record<string, codeBoxLanguagesType> = {
// JSON
"application/json": "json",
// XML
"application/xml": "xml",
"text/xml": "xml",
// HTML
"text/html": "html",
// Plain text
"text/plain": "plaintext",
// JavaScript
"application/javascript": "javascript",
"text/javascript": "javascript",
// CSS
"text/css": "css",
// YAML
"application/x-yaml": "yaml",
"text/yaml": "yaml",
// Others (fallback)
"*/*": "plaintext", // 任意类型默认处理为普通文本
};
@@ -749,5 +749,19 @@
"K67f4e9bb": "The domain name for obtaining API market documentation information when integrating with external platforms", "K67f4e9bb": "The domain name for obtaining API market documentation information when integrating with external platforms",
"K1da86266": "Invalid", "K1da86266": "Invalid",
"K3a34d49b": "Pending Update", "K3a34d49b": "Pending Update",
"Kd2850420": "Pending Deletion" "Kd2850420": "Pending Deletion",
"K9ada4366": "Operation successful, the page will refresh shortly",
"K9b332ab1": "Request prefix",
"K3d78d483": "HTTP headers",
"K17dc3a62": "Data logs",
"Ke429194e": "Processing logs",
"K84ffb1dd": "Enter the invocation address, consumer IP, and consumer condition to search",
"Kb147fabc": "Create",
"K40ca4f2": "Update",
"K3e7aa0ad": "Content",
"K2f5fdf5e": "Call Address",
"K1bc5e0a3": "Consumer IP",
"K6f39ea21": "Authentication Name",
"K8c34c02f": "Before Masking",
"K8e3d388d": "After Masking"
} }
@@ -771,6 +771,19 @@
"K67f4e9bb": "外部プラットフォームと統合する際に、API市場のドキュメント情報を取得するためのドメイン名", "K67f4e9bb": "外部プラットフォームと統合する際に、API市場のドキュメント情報を取得するためのドメイン名",
"K1da86266": "無効", "K1da86266": "無効",
"K3a34d49b": "更新待ち", "K3a34d49b": "更新待ち",
"Kd2850420": "削除待ち" "Kd2850420": "削除待ち",
"K9ada4366": "操作成功、ページを更新します",
"K9b332ab1": "リクエストプレフィックス",
"K3d78d483": "HTTPヘッダー",
"K17dc3a62": "データログ",
"Ke429194e": "ログの処理",
"K84ffb1dd": "呼び出しアドレス、コンシューマーIP、条件を入力して検索",
"Kb147fabc": "新規作成",
"K40ca4f2": "更新",
"K3e7aa0ad": "内容",
"K2f5fdf5e": "呼び出しアドレス",
"K1bc5e0a3": "コンシューマー IP",
"K6f39ea21": "認証名",
"K8c34c02f": "マスキング前",
"K8e3d388d": "マスキング後"
} }
@@ -702,5 +702,19 @@
"K31faa2a1": "优先级", "K31faa2a1": "优先级",
"Kbdec9fa": "筛选条件", "Kbdec9fa": "筛选条件",
"Kbcbb7391": "处理数", "Kbcbb7391": "处理数",
"Kad207008": "编辑" "Kad207008": "编辑",
"K9ada4366": "操作成功,即将刷新页面",
"K9b332ab1": "请求前缀",
"K3d78d483": "HTTP 头部",
"K17dc3a62": "数据日志",
"Ke429194e": "处理日志",
"K84ffb1dd": "输入调用地址、消费者IP和消费者条件查找",
"Kb147fabc": "新建",
"K40ca4f2": "更新",
"K3e7aa0ad": "内容",
"K2f5fdf5e": "调用地址",
"K1bc5e0a3": "消费者 IP",
"K6f39ea21": "鉴权名称",
"K8c34c02f": "脱敏前",
"K8e3d388d": "脱敏后"
} }
@@ -771,5 +771,19 @@
"K67f4e9bb": "與外部平台集成時,用於獲取 API 市場文檔信息的域名", "K67f4e9bb": "與外部平台集成時,用於獲取 API 市場文檔信息的域名",
"K1da86266": "無效", "K1da86266": "無效",
"K3a34d49b": "待更新", "K3a34d49b": "待更新",
"Kd2850420": "待刪除" "Kd2850420": "待刪除",
"K9ada4366": "操作成功,即將刷新頁面",
"K9b332ab1": "請求前綴",
"K3d78d483": "HTTP 標頭",
"K17dc3a62": "數據日誌",
"Ke429194e": "處理日誌",
"K84ffb1dd": "輸入調用地址、消費者 IP 和消費者條件進行查找",
"Kb147fabc": "新建",
"K40ca4f2": "更新",
"K3e7aa0ad": "內容",
"K2f5fdf5e": "調用地址",
"K1bc5e0a3": "消費者 IP",
"K6f39ea21": "鑑權名稱",
"K8c34c02f": "脫敏前",
"K8e3d388d": "脫敏後"
} }
@@ -1,115 +1,157 @@
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { COLUMNS_TITLE } from "@common/const/const";
import { EntityItem } from "@common/const/type"; import { EntityItem } from "@common/const/type";
export type PartitionConfigFieldType = { export type PartitionConfigFieldType = {
name?: string; name?: string;
id?: string; id?: string;
description?: string; description?: string;
prefix?:string prefix?: string
url?:string url?: string
managerAddress?:string managerAddress?: string
canDelete?:boolean canDelete?: boolean
}; };
export type PartitionCertTableListItem = { export type PartitionCertTableListItem = {
id:string; id: string;
name: string; name: string;
domains:string[]; domains: string[];
notAfter:string; notAfter: string;
notBefore:string; notBefore: string;
updater:EntityItem; updater: EntityItem;
updateTime:string; updateTime: string;
}; };
export type PartitionCertConfigFieldType = { export type PartitionCertConfigFieldType = {
id?:string id?: string
key:string key: string
pem:string pem: string
}; };
export type PartitionCertConfigProps = { export type PartitionCertConfigProps = {
type:'add'|'edit' type: 'add' | 'edit'
entity?:PartitionCertConfigFieldType entity?: PartitionCertConfigFieldType
} }
export type PartitionCertConfigHandle = { export type PartitionCertConfigHandle = {
save:()=>Promise<boolean|string> save: () => Promise<boolean | string>
} }
export type PartitionClusterFieldType = { export type PartitionClusterFieldType = {
name?: string; name?: string;
id?: string; id?: string;
description?: string; description?: string;
address?:string; address?: string;
protocol?:'http'|'https' protocol?: 'http' | 'https'
}; };
export type ClusterConfigProps = { export type ClusterConfigProps = {
mode:'config' | 'retry' | 'result' | 'edit', mode: 'config' | 'retry' | 'result' | 'edit',
clusterId?:string clusterId?: string
initFormValue?:{[k:string]:string|number} initFormValue?: { [k: string]: string | number }
} }
export type ClusterConfigHandle = { export type ClusterConfigHandle = {
save:()=>Promise<boolean|string> save: () => Promise<boolean | string>
check:()=>Promise<boolean> check: () => Promise<boolean>
} }
export type PartitionClusterTableListItem = { export type PartitionClusterTableListItem = {
id:string; id: string;
name: string; name: string;
status:0|1; status: 0 | 1;
description:string; description: string;
}; };
export type PartitionClusterNodeTableListItem = { export type PartitionClusterNodeTableListItem = {
id:string; id: string;
name: string; name: string;
managerAddress:string[]; managerAddress: string[];
serviceAddress:string[]; serviceAddress: string[];
peerAddress:string[]; peerAddress: string[];
status:0|1; status: 0 | 1;
}; };
export type PartitionClusterNodeModalTableListItem = { export type PartitionClusterNodeModalTableListItem = {
id: string, id: string,
name: string, name: string,
managerAddress: [], managerAddress: [],
serviceAddress: [], serviceAddress: [],
peerAddress: string, peerAddress: string,
status: string status: string
} }
export type NodeModalFieldType = { export type NodeModalFieldType = {
address:string address: string
} }
export type NodeModalHandle = { export type NodeModalHandle = {
save:()=>void save: () => void
} }
export type NodeModalPropsType = { export type NodeModalPropsType = {
changeStatus:(status:ClusterPageShowStatus)=>void changeStatus: (status: ClusterPageShowStatus) => void
getClusterInfo:()=>void getClusterInfo: () => void
status:ClusterPageShowStatus status: ClusterPageShowStatus
} }
export type ClusterPageShowStatus = 'view'|'preview'|'edit' export type ClusterPageShowStatus = 'view' | 'preview' | 'edit'
export type PartitionTableListItem = { export type PartitionTableListItem = {
id:string; id: string;
name: string; name: string;
clusterNum:number; clusterNum: number;
updater:EntityItem; updater: EntityItem;
updateTime:string; updateTime: string;
}; };
export type SimplePartition = EntityItem & { clusters: (EntityItem & {description:string})[] } export type SimplePartition = EntityItem & { clusters: (EntityItem & { description: string })[] }
export type PartitionDashboardConfigFieldType = { export type PartitionDashboardConfigFieldType = {
driver:string driver: string
config:{ config: {
org:string org: string
token:string token: string
addr:string addr: string
}
}
export type PartitionDataLogHeaderListFieldType = {
key: string
value: string
}
export type PartitionDataLogConfigFieldType = {
headers: PartitionDataLogHeaderListFieldType[]
url: string
}
export const PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS: PageProColumns<PartitionDataLogConfigFieldType & { _id: string }>[] = [
{
title: ('标签'),
dataIndex: 'key',
formItemProps: {
className: 'p-0 bg-transparent border-none',
rootClassName: 'test',
rules: [
{
required: true,
whitespace: true
},
],
},
ellipsis: true
},
{
title: ('内容'),
dataIndex: 'value',
formItemProps: {
className: 'p-0 bg-transparent border-none'
} }
} },
{
title: COLUMNS_TITLE.operate,
valueType: 'option',
btnNums: 2,
render: () => null
},
];
@@ -239,7 +239,7 @@ export const AiServiceSubscriberConfig = forwardRef<AiServiceSubscriberConfigHan
getAiServiceList() getAiServiceList()
}, [serviceId]); }, [serviceId]);
return (<WithPermission access="team.service.subscription.add"> return (<WithPermission access="">
<Form <Form
layout='vertical' layout='vertical'
labelAlign='left' labelAlign='left'
@@ -106,9 +106,9 @@ const AiSettingList = ()=>{
</a> </a>
<div> <div>
<CancelBtn/> <CancelBtn/>
<WithPermission access="system.devops.ai_provider.edit" showDisabled={false}> {
<OkBtn/> checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn/> : null
</WithPermission> }
</div> </div>
</div> </div>
); );
@@ -3,7 +3,7 @@ import { useEffect } from "react";
import { PartitionDashboardConfigFieldType } from "../../const/partitions/types"; import { PartitionDashboardConfigFieldType } from "../../const/partitions/types";
import { DASHBOARD_SETTING_DRIVER_OPTION_LIST } from "../../const/partitions/const"; import { DASHBOARD_SETTING_DRIVER_OPTION_LIST } from "../../const/partitions/const";
import WithPermission from "@common/components/aoplatform/WithPermission"; import WithPermission from "@common/components/aoplatform/WithPermission";
import { BasicResponse, PLACEHOLDER, STATUS_CODE, VALIDATE_MESSAGE } from "@common/const/const"; import { BasicResponse, PLACEHOLDER, STATUS_CODE } from "@common/const/const";
import { useFetch } from "@common/hooks/http"; import { useFetch } from "@common/hooks/http";
import { $t } from "@common/locales"; import { $t } from "@common/locales";
@@ -29,10 +29,10 @@ export type DashboardSettingEditProps = {
fetchData<BasicResponse<{info: PartitionDashboardConfigFieldType}>>('monitor/config',{method: 'POST',body:JSON.stringify(value),eoParams:{}}).then(response=>{ fetchData<BasicResponse<{info: PartitionDashboardConfigFieldType}>>('monitor/config',{method: 'POST',body:JSON.stringify(value),eoParams:{}}).then(response=>{
const {code,msg} = response const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){ if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功,即将刷新页面') message.success(msg || $t('操作成功,即将刷新页面'))
refreshData?.() refreshData?.()
}else{ }else{
message.error(msg || '操作失败') message.error(msg || $t('操作失败'))
} }
}) })
}) })
@@ -0,0 +1,90 @@
import EditableTable from "@common/components/aoplatform/EditableTable"
import WithPermission from "@common/components/aoplatform/WithPermission"
import { BasicResponse, PLACEHOLDER, STATUS_CODE } from "@common/const/const"
import { useFetch } from "@common/hooks/http"
import { $t } from "@common/locales"
import { PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS, PartitionDataLogConfigFieldType, PartitionDataLogHeaderListFieldType } from "@core/const/partitions/types"
import { Button, Form, Input, message } from "antd"
import { useEffect } from "react"
export type DashboardPageShowStatus = 'view' | 'edit'
export type DashboardSettingEditProps = {
changeStatus: (status: DashboardPageShowStatus) => void
refreshData: () => void
data?: PartitionDataLogConfigFieldType
}
const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
const { changeStatus, refreshData, data } = props
const [form] = Form.useForm();
const { fetchData } = useFetch()
const onFinish = () => {
form.validateFields().then((value) => {
const formData = {
config: {
url: value.url,
headers: value.headers.filter((item: PartitionDataLogHeaderListFieldType) => item.key).map((item: PartitionDataLogHeaderListFieldType) => ({key:item.key, value:item.value || ''}))
}
}
fetchData<BasicResponse<{ info: PartitionDataLogConfigFieldType }>>('log/loki', { method: 'POST', body: JSON.stringify(formData), eoParams: {} }).then(response => {
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || $t('操作成功,即将刷新页面'))
refreshData?.()
} else {
message.error(msg || $t('操作失败'))
}
})
})
}
useEffect(() => { form.setFieldsValue(data) }, [data])
useEffect(() => {
return (form.setFieldsValue({}))
}, []);
return (
<>
<div className="overflow-auto h-full">
<WithPermission access={''} >
<Form
form={form}
className="mx-auto flex flex-col justify-between h-full"
layout="vertical"
onFinish={onFinish}
autoComplete="off"
>
<Form.Item<PartitionDataLogConfigFieldType>
label={$t("请求前缀")}
name="url"
rules={[{ required: true }]}
>
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
</Form.Item>
<Form.Item<PartitionDataLogConfigFieldType>
label={$t("HTTP 头部")}
name="headers"
>
<EditableTable<PartitionDataLogConfigFieldType & { _id: string }>
configFields={PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS}
/>
</Form.Item>
<div className="flex gap-btnbase">
<WithPermission access='system.devops.data_source.edit'>
<Button type="primary" htmlType="submit">
{$t('保存')}
</Button>
</WithPermission>
<Button type="default" onClick={() => changeStatus('view')}>
{$t('取消')}
</Button>
</div>
</Form>
</WithPermission>
</div>
</>
);
}
export default DataLogSettingEdit;
@@ -57,7 +57,7 @@ const CertConfigModal = forwardRef<PartitionCertConfigHandle,PartitionCertConfig
} }
}, []); }, []);
return (<WithPermission access={type === 'edit' ? 'system.devops.ssl_certificate.edit':'system.devops.ssl_certificate.add'}> return (<WithPermission access=''>
<Form <Form
layout='vertical' layout='vertical'
labelAlign='left' labelAlign='left'
@@ -187,7 +187,7 @@ const PartitionInsideCert:FC = ()=>{
switch (type){ switch (type){
case 'add': case 'add':
title=$t('添加证书') title=$t('添加证书')
content= <CertConfigModal ref={addRef} type="add"/> content= <WithPermission access='system.devops.ssl_certificate.add'><CertConfigModal ref={addRef} type="add"/></WithPermission>
break; break;
case 'edit':{ case 'edit':{
title=$t('修改证书') title=$t('修改证书')
@@ -195,7 +195,7 @@ const PartitionInsideCert:FC = ()=>{
const {code,data,msg} = await fetchData<BasicResponse<{cert:{key:string, pem:string}}>>('certificate',{method:'GET',eoParams:{id:entity!.id}}) const {code,data,msg} = await fetchData<BasicResponse<{cert:{key:string, pem:string}}>>('certificate',{method:'GET',eoParams:{id:entity!.id}})
message.destroy() message.destroy()
if(code === STATUS_CODE.SUCCESS){ if(code === STATUS_CODE.SUCCESS){
content= <CertConfigModal ref={editRef} type="edit" entity={{...data.cert,id:entity!.id}}/> content= <WithPermission access={'system.devops.ssl_certificate.edit'}><CertConfigModal ref={editRef} type="edit" entity={{...data.cert,id:entity!.id}}/></WithPermission>
}else{ }else{
message.error(msg || $t(RESPONSE_TIPS.error)) message.error(msg || $t(RESPONSE_TIPS.error))
return return
@@ -1,93 +1,175 @@
import { FC, useEffect, useState} from "react"; import { FC, useEffect, useState } from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx"; import { useBreadcrumb } from "@common/contexts/BreadcrumbContext.tsx";
import {App, Button, Card, Col, Row, Spin, Tag} from "antd"; import { App, Button, Card, Col, Row, Spin, Tag } from "antd";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx"; import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts"; import { useFetch } from "@common/hooks/http.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx"; import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { LoadingOutlined } from "@ant-design/icons"; import { LoadingOutlined } from "@ant-design/icons";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx"; import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
import { $t } from "@common/locales/index.ts"; import { $t } from "@common/locales/index.ts";
import DashboardSettingEdit, { DashboardPageShowStatus } from "./DashboardSettingEdit.tsx"; import DashboardSettingEdit, { DashboardPageShowStatus } from "./DashboardSettingEdit.tsx";
import { PartitionDashboardConfigFieldType } from "@core/const/partitions/types.ts"; import { PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS, PartitionDashboardConfigFieldType, PartitionDataLogConfigFieldType } from "@core/const/partitions/types.ts";
import PageList from "@common/components/aoplatform/PageList.tsx";
import DataLogSettingEdit from "./DataLogSettingEdit.tsx";
const PartitionInsideDashboardSetting:FC = ()=> { const PartitionInsideDashboardSetting: FC = () => {
const {setBreadcrumb} = useBreadcrumb() const { setBreadcrumb } = useBreadcrumb()
const {message} = App.useApp() const { message } = App.useApp()
const {fetchData} = useFetch() const { fetchData } = useFetch()
const [data, setData] = useState<PartitionDashboardConfigFieldType>() const [data, setData] = useState<PartitionDashboardConfigFieldType>()
const [loading, setLoading] = useState<boolean>(false) const [dataLogData, setDataLogData] = useState<PartitionDataLogConfigFieldType>()
const [showStatus, setShowStatus] = useState<DashboardPageShowStatus>('view') const [loading, setLoading] = useState<boolean>(false)
const [dataLogLoading, setDataLogLoading] = useState<boolean>(false)
const [showGraphStatus, setShowGraphStatus] = useState<DashboardPageShowStatus>('view')
const [showDataLogStatus, setShowDataLogStatus] = useState<DashboardPageShowStatus>('view')
const getDashboardSettingInfo = () => { const getDashboardSettingInfo = () => {
setLoading(true) setLoading(true)
return fetchData<BasicResponse<{ nodes:PartitionDashboardConfigFieldType[] }>>('monitor/config', {method: 'GET',eoTransformKeys:[]}).then(response => { return fetchData<BasicResponse<{ nodes: PartitionDashboardConfigFieldType[] }>>('monitor/config', { method: 'GET', eoTransformKeys: [] }).then(response => {
const {code, data, msg} = response const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) { if (code === STATUS_CODE.SUCCESS) {
data?.info?.driver && setData(data.info) data?.info?.driver && setData(data.info)
setShowStatus('view') setShowGraphStatus('view')
} else { } else {
message.error(msg || $t(RESPONSE_TIPS.error)) message.error(msg || $t(RESPONSE_TIPS.error))
} }
}).catch(() => { }).catch(() => {
return {data: [], success: false} return { data: [], success: false }
}).finally(()=>{ }).finally(() => {
setLoading(false) setLoading(false)
})
}
const getDataLogSettingInfo = () => {
setDataLogLoading(true)
return fetchData<BasicResponse<{ nodes: PartitionDataLogConfigFieldType[] }>>('log/loki', { method: 'GET', eoTransformKeys: [] }).then(response => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
data?.info && setDataLogData({
url: data.info?.config?.url || '',
headers: data.info?.config?.headers || []
}) })
} setShowDataLogStatus('view')
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
}).catch(() => {
return { data: [], success: false }
}).finally(() => {
setDataLogLoading(false)
})
}
useEffect(() => {
setBreadcrumb([
{ title: $t('数据源') }
])
getDashboardSettingInfo()
getDataLogSettingInfo()
}, []);
useEffect(() => { const setDashboardSettingBtn = () => {
setBreadcrumb([ return (<>
{title: $t('数据源')} {showGraphStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
]) <Button type="primary" onClick={() => setShowGraphStatus('edit')}>{$t('修改配置')}</Button>
getDashboardSettingInfo() </WithPermission>}</>
}, []);
const setDashboardSettingBtn = ()=>{
return (<>
{showStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
<Button type="primary" onClick={() => setShowStatus('edit')}>{$t('修改配置')}</Button>
</WithPermission> }</>
)
}
return (
<>
<InsidePage
pageTitle={$t('数据源')}
description={$t("设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。")}
showBorder={false}
scrollPage={true}
>
<div className="flex flex-col h-full overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
<Spin wrapperClassName=" h-full flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
<div className="h-full overflow-auto">
<Card
classNames={{
body: `overflow-auto ${(!data || !data?.driver) && showStatus === 'view' ? 'hidden': ''}`,
}}
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('统计图表')}</span>
{!loading && !data?.driver && <Tag color='#f50'>{ $t('未配置')}
</Tag>}</div>}
extra={setDashboardSettingBtn()}>
{showStatus === 'view'&& data && data.driver && DashboardConfigPreview(data) }
{showStatus !== 'view' && <DashboardSettingEdit data={data} changeStatus={setShowStatus} refreshData={getDashboardSettingInfo} />}
</Card>
</div>
</Spin>
</div>
</InsidePage>
</>
) )
}
const setDataLogSettingBtn = () => {
return (<>
{showDataLogStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
<Button type="primary" onClick={() => setShowDataLogStatus('edit')}>{$t('修改配置')}</Button>
</WithPermission>}</>
)
}
return (
<>
<InsidePage
pageTitle={$t('数据源')}
description={$t("设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。")}
showBorder={false}
scrollPage={false}
>
<div className="flex flex-col overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
<Spin wrapperClassName="flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading}>
<div className="h-full overflow-auto">
<Card
classNames={{
body: `overflow-auto ${(!data || !data?.driver) && showGraphStatus === 'view' ? 'hidden' : ''}`,
}}
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('统计图表')}</span>
{!loading && !data?.driver && <Tag color='#f50'>{$t('未配置')}
</Tag>}</div>}
extra={setDashboardSettingBtn()}>
{showGraphStatus === 'view' && data && data.driver && DashboardConfigPreview(data)}
{showGraphStatus !== 'view' && <DashboardSettingEdit data={data} changeStatus={setShowGraphStatus} refreshData={getDashboardSettingInfo} />}
</Card>
</div>
</Spin>
<Spin wrapperClassName="flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={dataLogLoading}>
<div className="h-full overflow-auto">
<Card
classNames={{
body: `overflow-auto ${(!dataLogData) && showDataLogStatus === 'view' ? 'hidden' : ''}`,
}}
className="overflow-hidden mt-[30px] w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('数据日志')}</span>
{!dataLogLoading && !dataLogData && <Tag color='#f50'>{$t('未配置')}
</Tag>}</div>}
extra={setDataLogSettingBtn()}>
{showDataLogStatus === 'view' && dataLogData && DataLogConfigPreview(dataLogData)}
{showDataLogStatus !== 'view' && <DataLogSettingEdit data={dataLogData} changeStatus={setShowDataLogStatus} refreshData={getDataLogSettingInfo} />}
</Card>
</div>
</Spin>
</div>
</InsidePage>
</>
)
} }
export function DashboardConfigPreview (x:PartitionDashboardConfigFieldType){ export function DashboardConfigPreview(x: PartitionDashboardConfigFieldType) {
return <div className="flex flex-col gap-[4px] "> return <div className="flex flex-col gap-[4px] ">
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('数据源')}</Col><Col>{x?.driver}</Col></Row> <Row className=""><Col className="font-bold text-right pr-[4px]">{$t('数据源')}</Col><Col>{x?.driver}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('地址(IP:端口)')}</Col><Col>{x?.config?.addr}</Col></Row> <Row className=""><Col className="font-bold text-right pr-[4px]">{$t('地址(IP:端口)')}</Col><Col>{x?.config?.addr}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('组织(Organization')}</Col><Col>{x?.config?.org}</Col></Row> <Row className=""><Col className="font-bold text-right pr-[4px]">{$t('组织(Organization')}</Col><Col>{x?.config?.org}</Col></Row>
</div>} </div>
}
export function DataLogConfigPreview(x: PartitionDataLogConfigFieldType) {
const columns = PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS.map(x => {
return {
...x,
title: (<span title={$t(x.title as string)}>{$t(x.title as string)}</span>)
}
})
const getTableList = () => {
return new Promise((resolve, reject) => {
resolve({
data: x?.headers || [],
success: true
})
})
}
return <div className="flex flex-col gap-[4px] ">
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('请求前缀')}</Col><Col>{x?.url}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('HTTP 头部')}</Col><Col className="w-full">
<div className="w-full h-full p-[20px]">
<PageList
id="global_role"
tableClass="role_table mb-btnrbase"
primaryKey="'key'"
columns={[...columns]}
request={() => getTableList()}
showPagination={false}
noScroll={true}
/>
</div>
</Col></Row>
</div>
}
export default PartitionInsideDashboardSetting export default PartitionInsideDashboardSetting
@@ -79,7 +79,7 @@ const DataMasking = (props: any) => {
{ {
title: '', title: '',
key: 'option', key: 'option',
btnNums: rowOperation.length -1, btnNums: rowOperation.length,
fixed: 'right', fixed: 'right',
valueType: 'option', valueType: 'option',
render: (_: React.ReactNode, entity: any) => [ render: (_: React.ReactNode, entity: any) => [
@@ -233,7 +233,6 @@ const DataMasking = (props: any) => {
* @param entity * @param entity
*/ */
const openLogsModal = (entity: any) => { const openLogsModal = (entity: any) => {
console.log('日志', entity);
setStrategy(entity.id) setStrategy(entity.id)
setModalVisible(true) setModalVisible(true)
} }
@@ -379,10 +378,11 @@ const DataMasking = (props: any) => {
<Modal <Modal
title={$t('处理日志')} title={$t('处理日志')}
visible={modalVisible} visible={modalVisible}
destroyOnClose={true}
onCancel={handleCloseModal} onCancel={handleCloseModal}
footer={null} footer={null}
wrapClassName="modal-without-footer" wrapClassName="modal-without-footer"
width={1000} width={1100}
maskClosable={true} maskClosable={true}
> >
<div className="pb-btnybase flex flex-nowrap flex-col h-full w-full items-center justify-between"> <div className="pb-btnybase flex flex-nowrap flex-col h-full w-full items-center justify-between">
@@ -27,7 +27,7 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
filters: true, filters: true,
onFilter: false , onFilter: false ,
ellipsis: true, ellipsis: true,
width: 140, width: 110,
valueEnum: new Map( valueEnum: new Map(
Object.keys(StrategyStatusEnum).map(key=> Object.keys(StrategyStatusEnum).map(key=>
[key, [key,
@@ -40,6 +40,7 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
filters: true, filters: true,
onFilter: false , onFilter: false ,
ellipsis: true, ellipsis: true,
width: 90,
valueEnum: { valueEnum: {
false: { text: <span className="text-status_success">{$t('启用')}</span> }, false: { text: <span className="text-status_success">{$t('启用')}</span> },
true: { text: <span className="text-status_fail">{$t('禁用')}</span> } true: { text: <span className="text-status_fail">{$t('禁用')}</span> }
@@ -53,7 +54,8 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
{ {
title: ('处理数'), title: ('处理数'),
dataIndex: 'processedTotal', dataIndex: 'processedTotal',
ellipsis: true ellipsis: true,
width: 80,
}, },
{ {
title: ('更新者'), title: ('更新者'),
@@ -83,10 +85,10 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
width: 200 width: 200
}, },
{ {
title: ('消费者IP'), title: ('消费者 IP'),
dataIndex: 'remote_ip', dataIndex: 'remote_ip',
ellipsis: true, ellipsis: true,
width: 150 width: 100
}, },
{ {
title: ('消费者'), title: ('消费者'),
@@ -96,14 +98,14 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
}, },
{ {
title: ('鉴权名称'), title: ('鉴权名称'),
dataIndex: 'authorization', dataIndex: ['authorization', 'name'],
ellipsis: true, ellipsis: true,
width: 100 width: 100
}, },
{ {
title: ('时间'), title: ('时间'),
dataIndex: 'record_time', dataIndex: 'record_time',
width: 150, width: 110,
ellipsis: true ellipsis: true
}, },
] ]
@@ -1,13 +1,12 @@
import { Codebox } from "@common/components/postcat/api/Codebox"; import { Codebox, codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const"; import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { RouterParams } from "@common/const/type";
import { $t } from "@common/locales"; import { $t } from "@common/locales";
import { App, Button, message, Switch, Modal, Spin } from 'antd' import { message, Spin } from 'antd'
import { useFetch } from "@common/hooks/http"; import { useFetch } from "@common/hooks/http";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { LoadingOutlined } from "@ant-design/icons"; import { LoadingOutlined } from "@ant-design/icons";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { contentTypeToLanguageMap } from "@common/const/policy/consts";
type LogItems = { type LogItems = {
id: string; id: string;
origin: string; origin: string;
@@ -18,7 +17,15 @@ const DataMaskingCompare = () => {
const { fetchData } = useFetch() const { fetchData } = useFetch()
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [originValue, setOriginValue] = useState('') const [originValue, setOriginValue] = useState('')
const [targetValue, settTargetValue] = useState('') const [targetValue, setTargetValue] = useState('')
const [language, setLanguage] = useState<codeBoxLanguagesType>('json')
const getMonacoEditorLanguage = (contentType: string): codeBoxLanguagesType => {
// 提取主类型,忽略参数(如 "; charset=utf-8"
const mainType = contentType.split(";")[0].trim().toLowerCase();
// 根据映射表获取语言,默认返回 "plaintext"
return contentTypeToLanguageMap[mainType] || "json";
};
const getLogData = () => { const getLogData = () => {
setLoading(true) setLoading(true)
return fetchData<BasicResponse<{ log: LogItems }>>(`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/log`, return fetchData<BasicResponse<{ log: LogItems }>>(`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/log`,
@@ -33,8 +40,10 @@ const DataMaskingCompare = () => {
const { code, data, msg } = response const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) { if (code === STATUS_CODE.SUCCESS) {
const { log } = data const { log } = data
setOriginValue(log.origin || '') const docLanguage = getMonacoEditorLanguage(log.content_type)
settTargetValue(log.target || '') setLanguage(docLanguage)
setOriginValue(docLanguage === 'json' ? JSON.stringify(JSON.parse(log.origin || ''), null, 2) : log.origin || '')
setTargetValue(docLanguage === 'json' ? JSON.stringify(JSON.parse(log.target || ''), null, 2) : log.target || '')
setLoading(false) setLoading(false)
} else { } else {
message.error(msg || $t(RESPONSE_TIPS.error)) message.error(msg || $t(RESPONSE_TIPS.error))
@@ -42,17 +51,6 @@ const DataMaskingCompare = () => {
}).catch(() => { }).catch(() => {
return { data: [], success: false } return { data: [], success: false }
}).finally(() => { }).finally(() => {
const aa = `{
"code": {
"gg": "gg",
"gg1": "gg",
"gg2": "gg",
"gg3": "gg",
"gg4": "gg"
}
}`
setOriginValue(JSON.stringify(JSON.parse(aa), null, 2))
settTargetValue(JSON.stringify(JSON.parse(aa), null, 2))
setLoading(false) setLoading(false)
}) })
} }
@@ -64,24 +62,26 @@ const DataMaskingCompare = () => {
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
<div className="w-1/2 p-2 h-full"> <div className="w-1/2 p-2 h-full">
<div className="h-[30px] bg-gray-200 mb-2 flex items-center justify-center"> <div className="h-[30px] bg-gray-200 mb-2 flex items-center justify-center">
{$t('脱敏前')}
</div> </div>
<div style={{ height: 'calc(100vh - 50px)' }}> <div style={{ height: 'calc(100vh - 50px)' }}>
<Codebox <Codebox
language='json' language={language}
height="100%" height="100%"
width="100%" width="100%"
value={originValue} value={originValue}
sx={{ whiteSpace: 'nowrap' }}
readOnly
/> />
</div> </div>
</div> </div>
<div className="w-1/2 p-2 h-full"> <div className="w-1/2 p-2 h-full">
<div className="h-[30px] bg-green-100 mb-2 flex items-center justify-center"> <div className="h-[30px] bg-green-100 mb-2 flex items-center justify-center">
{$t('脱敏后')}
</div> </div>
<div style={{ height: 'calc(100vh - 50px)' }}> <div style={{ height: 'calc(100vh - 50px)' }}>
<Codebox <Codebox
language='json' language={language}
width="100%" width="100%"
height="100%" height="100%"
value={targetValue} value={targetValue}
@@ -1,46 +1,62 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useMemo, useRef, useState } from 'react';
import { DataMaskLogItem } from "@common/const/policy/type"; import { DataMaskLogItem } from "@common/const/policy/type";
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList"; import PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
import { $t } from "@common/locales"; import { $t } from "@common/locales";
import { App, Button, message, DatePicker, Modal } from 'antd' import { Button, message } from 'antd'
import { DATA_MASKING_TABLE_LOG_COLUMNS, DATA_MASKING_TABLE_COLUMNS } from './DataMaskingColumn'; import { DATA_MASKING_TABLE_LOG_COLUMNS } from './DataMaskingColumn';
import { useGlobalContext } from '@common/contexts/GlobalStateContext'; import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import { ActionType } from '@ant-design/pro-components'; import { ActionType } from '@ant-design/pro-components';
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'; import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { RouterParams } from '@common/const/type'; import { RouterParams } from '@common/const/type';
import { useFetch } from '@common/hooks/http'; import { useFetch } from '@common/hooks/http';
import WithPermission from '@common/components/aoplatform/WithPermission';
import TimeRangeSelector, { TimeRange } from '@common/components/aoplatform/TimeRangeSelector'; import TimeRangeSelector, { TimeRange } from '@common/components/aoplatform/TimeRangeSelector';
import { SearchBody } from '@dashboard/const/type'; import { SearchBody } from '@dashboard/const/type';
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission'; import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission';
const { RangePicker } = DatePicker;
const DataMaskingLogModal = (props: any) => { const DataMaskingLogModal = (props: any) => {
const { strategy } = props; const { strategy } = props;
const { state, accessData } = useGlobalContext() const { state, accessData } = useGlobalContext()
const { serviceId, teamId } = useParams<RouterParams>() const { serviceId, teamId } = useParams<RouterParams>()
const [datePickerValue, setDatePickerValue] = useState<any>(); const [datePickerValue, setDatePickerValue] = useState<any>();
const [queryData, setQueryData] = useState<SearchBody>({}) const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
const [queryData, setQueryData] = useState<SearchBody>({
start: currentSecond - 24 * 60 * 60,
end: currentSecond
})
/** /**
* *
*/ */
const { fetchData } = useFetch() const { fetchData } = useFetch()
/** /**
* ref * ref
*/ */
const pageListRef = useRef<ActionType>(null); const pageListRef = useRef<ActionType>(null);
/** /**
* *
*/ */
const [searchWord, setSearchWord] = useState<string>('') const [searchWord, setSearchWord] = useState<string>('')
/**
*
*/
let resetTimeRange = () => {}
/**
*
*/
const [timeButton, setTimeButton] = useState<'' | 'hour' | 'day' | 'threeDays' | 'sevenDays'>('day');
/**
*
* @param instance
*/
const bindRef = (instance: any) => {
resetTimeRange = instance.reset
};
/** /**
* *
*/ */
const operation: PageProColumns<any>[] = [ const operation: PageProColumns<any>[] = [
{ {
title: '操作', title: '',
key: 'option', key: 'option',
btnNums: 1, btnNums: 1,
fixed: 'right', fixed: 'right',
@@ -54,7 +70,7 @@ const DataMaskingLogModal = (props: any) => {
url += `/${teamId}` url += `/${teamId}`
} }
return [ return [
<TableBtnWithPermission access={`${serviceId === undefined ? 'system.devops' : 'team.service'}.policy.view`} key="view" btnType="view" onClick={() => { window.open(url,'_blank') }} btnTitle="查看" /> <TableBtnWithPermission access={`${serviceId === undefined ? 'system.devops' : 'team.service'}.policy.view`} key="view" btnType="view" onClick={() => { window.open(url, '_blank') }} btnTitle="查看" />
] ]
} }
} }
@@ -68,11 +84,11 @@ const DataMaskingLogModal = (props: any) => {
const columns = useMemo(() => { const columns = useMemo(() => {
const res = DATA_MASKING_TABLE_LOG_COLUMNS.map(x => { const res = DATA_MASKING_TABLE_LOG_COLUMNS.map(x => {
if (x.dataIndex === 'url') { if (x.dataIndex === 'url') {
x.render = (text: any, record: any) => <><span className='text-green-500'>{record.method}</span>&nbsp;<span>{text}</span></> x.render = (text: any, record: any) => <><div className='w-full'><span className='text-green-500'>{record.method}</span>&nbsp;<span className='w-[calc(100%-25px)] text-ellipsis overflow-hidden whitespace-nowrap inline-block align-top'>{text}</span></div></>
} }
return { return {
...x, ...x,
title: typeof x.title === 'string' ? $t(x.title as string) : x.title title: (<span title={$t(x.title as string)}>{$t(x.title as string)}</span>)
} }
}) })
return res return res
@@ -88,7 +104,7 @@ const DataMaskingLogModal = (props: any) => {
current: number; current: number;
}) => { }) => {
return fetchData<BasicResponse<{ logs: DataMaskLogItem[], total: number }>>( return fetchData<BasicResponse<{ logs: DataMaskLogItem[], total: number }>>(
`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/list`, `strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/logs`,
{ {
method: 'GET', method: 'GET',
eoParams: { eoParams: {
@@ -106,204 +122,9 @@ const DataMaskingLogModal = (props: any) => {
).then(response => { ).then(response => {
const { code, data, msg } = response const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) { if (code === STATUS_CODE.SUCCESS) {
const mockData: any = [
{
id: '12334',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff1',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff2',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff3',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff4',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff5',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff6',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff7',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff8',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff9',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff11',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:12',
},
{
id: 'fff22',
service: {
id: 'xxx',
name: 'xxx'
},
url: 'url',
remote_ip: '9234923',
consumer: {
id: 'yyy',
name: 'yyy'
},
method: 'GET',
authorization: 'authorization',
record_time: '2021-09-09 12:12:11',
}
]
// 保存数据 // 保存数据
return { return {
data: mockData, data: data.logs || [],
total: data.total, total: data.total,
success: true success: true
} }
@@ -320,24 +141,13 @@ const DataMaskingLogModal = (props: any) => {
const handleTimeRangeChange = (timeRange: TimeRange) => { const handleTimeRangeChange = (timeRange: TimeRange) => {
setQueryData(pre => ({ ...pre, ...timeRange } as SearchBody)) setQueryData(pre => ({ ...pre, ...timeRange } as SearchBody))
manualReloadTable() manualReloadTable()
}; };
const handleDatePickerChange = (dates: any) => {
if (dates && Array.isArray(dates) && dates.length === 2) {
const [startDate, endDate] = dates;
const start = startDate!.startOf('day').unix(); // 开始日期的00:00:00
const end = endDate!.endOf('day').unix(); // 结束日期的23:59:59
handleTimeRangeChange({ start, end });
} else {
handleTimeRangeChange({ start: null, end: null})
}
}
const resetQuery = () => { const resetQuery = () => {
setDatePickerValue(null) resetTimeRange()
handleTimeRangeChange({ start: null, end: null})
}; };
return ( return (
<> <>
<div className="w-full h-full p-[20px]"> <div className="w-full h-full p-[20px]">
@@ -348,11 +158,17 @@ const DataMaskingLogModal = (props: any) => {
columns={[...columns, ...operation]} columns={[...columns, ...operation]}
afterNewBtn={ afterNewBtn={
[<div className="flex items-center flex-wrap p-[10px] px-btnbase content-before bg-MAIN_BG "> [<div className="flex items-center flex-wrap p-[10px] px-btnbase content-before bg-MAIN_BG ">
<RangePicker <TimeRangeSelector
onChange={handleDatePickerChange} labelSize="small"
value={datePickerValue} /> bindRef={bindRef}
<div className="flex [&>.reset-btn]:!h-auto flex-nowrap items-center ml-[10px]"> hideBtns={['hour']}
<Button className="reset-btn" onClick={resetQuery}>{$t('重置')}</Button> defaultTimeButton="day"
initialTimeButton={timeButton}
onTimeButtonChange={setTimeButton}
initialDatePickerValue={datePickerValue}
onTimeRangeChange={handleTimeRangeChange} />
<div className="flex flex-nowrap items-center pt-btnybase">
<Button onClick={resetQuery}>{$t('重置')}</Button>
</div> </div>
</div>] </div>]
} }
@@ -239,7 +239,7 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
getSystemList() getSystemList()
}, [serviceId]); }, [serviceId]);
return (<WithPermission access="team.service.subscription.add"> return (<WithPermission access="">
<Form <Form
layout='vertical' layout='vertical'
labelAlign='left' labelAlign='left'
+2 -1
View File
@@ -77,7 +77,8 @@ export default defineConfig({
target: 'http://172.18.166.219:8288/', target: 'http://172.18.166.219:8288/',
changeOrigin: true, changeOrigin: true,
} }
} },
open: true
}, },
logLevel:'info' logLevel:'info'
}) })
@@ -11,7 +11,7 @@ import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
import { SimpleTeamItem } from "@common/const/type"; import { SimpleTeamItem } from "@common/const/type";
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext"; import { useTenantManagementContext } from "../../../contexts/TenantManagementContext";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { useGlobalContext } from "@common/contexts/GlobalStateContext"; import { GlobalProvider, useGlobalContext } from "@common/contexts/GlobalStateContext";
import { $t } from "@common/locales"; import { $t } from "@common/locales";
import WithPermission from "@common/components/aoplatform/WithPermission"; import WithPermission from "@common/components/aoplatform/WithPermission";
import InsidePage from "@common/components/aoplatform/InsidePage"; import InsidePage from "@common/components/aoplatform/InsidePage";
@@ -149,7 +149,7 @@ export default function ServiceHubManagement() {
switch (type){ switch (type){
case 'add': case 'add':
title=$t('添加消费者') title=$t('添加消费者')
content=<ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} /> content=<GlobalProvider><ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} /></GlobalProvider>
break; break;
// case 'edit':{ // case 'edit':{
// title='配置 Open Api' // title='配置 Open Api'