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,
title: typeof x.title === 'string' ? $t(x.title) : x.title,
...(x.dataIndex === 'status' ? {
render:(_,entity)=>(
entity.status === 0 ? $t('正常') : $t('无效'))
render:(_,entity)=> (
<span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>
{$t(ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-')}
</span>
)
}:{})
}
}),[state.language])
@@ -37,6 +37,8 @@ const TableIconName = {
const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateTo, onClick, className, btnType }: TableBtnWithPermissionProps) => {
const [btnAccess, setBtnAccess] = useState<boolean>(false)
const [btnStatus, setBtnStatus] = useState<boolean>(false)
const [closeToolTip, setCloseToolTip] = useState<boolean>(false)
const { accessData, checkPermission, accessInit } = useGlobalContext()
const navigate = useNavigate()
const lastAccess = useMemo(() => {
@@ -52,16 +54,27 @@ const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateT
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
setTimeout(() => {
setBtnStatus(false)
setCloseToolTip(true)
})
navigateTo ? navigate(navigateTo) : onClick?.()
}, [navigateTo, navigate, onClick])
const changeTooltipStatus = (open: boolean) => {
setBtnStatus(open)
if (closeToolTip) {
setBtnStatus(false)
setCloseToolTip(false)
}
}
return (<>{
!btnAccess || (disabled && tooltip) ?
<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>
</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>
</Tooltip>
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
@@ -12,69 +12,80 @@ export type RangeValue = [Dayjs | null, Dayjs | null] | null;
dayjs.extend(customParseFormat);
export type TimeRange = {
start:number|null
end:number|null
start: number | null
end: number | null
}
export type TimeRangeButton = ''| 'hour' | 'day' | 'threeDays' | 'sevenDays';
export type TimeRangeButton = '' | 'hour' | 'day' | 'threeDays' | 'sevenDays';
type TimeRangeSelectorProps = {
initialTimeButton?:TimeRangeButton,
initialDatePickerValue?:RangeValue
onTimeRangeChange?:(timeRange:TimeRange) =>void
hideTitle?:boolean
onTimeButtonChange:(time:TimeRangeButton) =>void
labelSize?:'small'|'default'
}
const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
const {initialTimeButton,initialDatePickerValue,onTimeRangeChange,hideTitle,onTimeButtonChange,labelSize='default'} = props
initialTimeButton?: TimeRangeButton,
initialDatePickerValue?: RangeValue
onTimeRangeChange?: (timeRange: TimeRange) => void
hideTitle?: boolean
onTimeButtonChange: (time: TimeRangeButton) => void
labelSize?: 'small' | 'default'
bindRef?: any
hideBtns?: TimeRangeButton[]
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 [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 currentSecond = new Date().getTime() // 当前毫秒数时间戳
const currentMin = currentSecond - (currentSecond % (60 * 1000)) // 当前分钟数时间戳
let startMin = currentMin - 60 * 60 * 1000
const calculateTimeRange = (curBtn: TimeRangeButton) => {
const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
let startMin = currentSecond - 60 * 60
switch (curBtn) {
case 'hour': {
startMin = currentMin - 60 * 60 * 1000
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 'hour': {
startMin = currentSecond - 60 * 60
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) {
onTimeRangeChange({ start: startMin / 1000, end: currentMin / 1000 });
onTimeRangeChange({ start: startMin, end: currentSecond });
}
};
// 处理单选按钮的变化
const handleRadioChange = (e:RadioChangeEvent) => {
const handleRadioChange = (e: RadioChangeEvent) => {
setTimeButton(e.target.value);
onTimeButtonChange?.(e.target.value)
setDatePickerValue(null)
calculateTimeRange(e.target.value);
};
const reset = () => {
setTimeButton(defaultTimeButton)
calculateTimeRange(defaultTimeButton)
setDatePickerValue(null)
}
// 处理日期选择器的变化
const handleDatePickerChange = (dates: RangeValue) => {
setTimeButton(dates ? '' : 'hour')
onTimeButtonChange?.(dates ? '' : 'hour')
setTimeButton(dates ? '' : defaultTimeButton)
onTimeButtonChange?.(dates ? '' : defaultTimeButton)
setDatePickerValue(dates);
if (dates && Array.isArray(dates) && dates.length === 2) {
const [startDate, endDate] = dates;
@@ -84,34 +95,37 @@ const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
onTimeRangeChange({ start, end });
}
}
if (!dates) {
calculateTimeRange(defaultTimeButton)
}
};
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today
return current && current.valueOf() > dayjs().startOf('day').valueOf();
};
};
return (
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
<Radio.Button value="hour">{$t('近1小时')}</Radio.Button>
<Radio.Button value="day">{$t('近24小时')}</Radio.Button>
<Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>
<Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>
</Radio.Group>
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
{hideBtns?.length && hideBtns.includes('hour') ? null : <Radio.Button value="hour">{$t('近1小时')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('day') ? null : <Radio.Button value="day">{$t('近24小时')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('threeDays') ? null : <Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('sevenDays') ? null : <Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>}
</Radio.Group>
<DatePicker.RangePicker
value={datePickerValue}
className="rounded-s-none ml-[-1px]"
className="rounded-s-none ml-[-1px]"
disabledDate={disabledDate}
onChange={handleDatePickerChange}
onOpenChange={(open)=>{
if(!open && datePickerValue && datePickerValue.length > 2){
setTimeButton('')
onTimeButtonChange?.('')
}
onOpenChange={(open) => {
if (!open && datePickerValue && datePickerValue.length > 2) {
setTimeButton('')
onTimeButtonChange?.('')
}
}}
/>
</div>
@@ -181,6 +181,10 @@ export const TranslateWord = ()=>{
{$t('请输入IP地址或CIDR范围,每条以换行分割')}
{$t('待更新')}
{$t('待删除')}
{$t('内容')}
{$t('调用地址')}
{$t('消费者 IP')}
{$t('鉴权名称')}
</>
)
}
@@ -13,6 +13,7 @@ export interface CodeboxApiRef {
formatCode: () => void
}
export type codeBoxLanguagesType = 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
interface CodeboxProps {
options?: MonacoEditor.IStandaloneEditorConstructionOptions
value?: string
@@ -22,7 +23,7 @@ interface CodeboxProps {
height?: string | null
readOnly?: boolean
apiRef?: RefObject<CodeboxApiRef>
language?: 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
language?: codeBoxLanguagesType
extraContent?:React.ReactNode
sx?:Record<string,unknown>
editorTheme?:'vs' | 'vs-dark' | 'hc-black'
@@ -486,17 +486,17 @@ export const PERMISSION_DEFINITION = [
},
"team.application.subscription.add": {
"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": {
"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": {
"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": {
@@ -506,47 +506,47 @@ export const PERMISSION_DEFINITION = [
},
"team.application.application.add": {
"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": {
"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": {
"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": {
"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": {
"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": {
"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": {
"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": {
"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": {
"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": {
@@ -1,3 +1,4 @@
import { codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
export const MatchRules = [
{ value: 'inner', label: '数据格式' },
@@ -54,4 +55,34 @@ export const StrategyStatusEnum = {
"offline":'text-status_fail',
"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",
"K1da86266": "Invalid",
"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市場のドキュメント情報を取得するためのドメイン名",
"K1da86266": "無効",
"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": "优先级",
"Kbdec9fa": "筛选条件",
"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 市場文檔信息的域名",
"K1da86266": "無效",
"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";
export type PartitionConfigFieldType = {
name?: string;
id?: string;
description?: string;
prefix?:string
url?:string
managerAddress?:string
canDelete?:boolean
name?: string;
id?: string;
description?: string;
prefix?: string
url?: string
managerAddress?: string
canDelete?: boolean
};
export type PartitionCertTableListItem = {
id:string;
name: string;
domains:string[];
notAfter:string;
notBefore:string;
updater:EntityItem;
updateTime:string;
id: string;
name: string;
domains: string[];
notAfter: string;
notBefore: string;
updater: EntityItem;
updateTime: string;
};
export type PartitionCertConfigFieldType = {
id?:string
key:string
pem:string
id?: string
key: string
pem: string
};
export type PartitionCertConfigProps = {
type:'add'|'edit'
entity?:PartitionCertConfigFieldType
type: 'add' | 'edit'
entity?: PartitionCertConfigFieldType
}
export type PartitionCertConfigHandle = {
save:()=>Promise<boolean|string>
save: () => Promise<boolean | string>
}
export type PartitionClusterFieldType = {
name?: string;
id?: string;
description?: string;
address?:string;
protocol?:'http'|'https'
name?: string;
id?: string;
description?: string;
address?: string;
protocol?: 'http' | 'https'
};
export type ClusterConfigProps = {
mode:'config' | 'retry' | 'result' | 'edit',
clusterId?:string
initFormValue?:{[k:string]:string|number}
mode: 'config' | 'retry' | 'result' | 'edit',
clusterId?: string
initFormValue?: { [k: string]: string | number }
}
export type ClusterConfigHandle = {
save:()=>Promise<boolean|string>
check:()=>Promise<boolean>
save: () => Promise<boolean | string>
check: () => Promise<boolean>
}
export type PartitionClusterTableListItem = {
id:string;
name: string;
status:0|1;
description:string;
id: string;
name: string;
status: 0 | 1;
description: string;
};
export type PartitionClusterNodeTableListItem = {
id:string;
name: string;
managerAddress:string[];
serviceAddress:string[];
peerAddress:string[];
status:0|1;
id: string;
name: string;
managerAddress: string[];
serviceAddress: string[];
peerAddress: string[];
status: 0 | 1;
};
export type PartitionClusterNodeModalTableListItem = {
id: string,
name: string,
managerAddress: [],
serviceAddress: [],
peerAddress: string,
status: string
id: string,
name: string,
managerAddress: [],
serviceAddress: [],
peerAddress: string,
status: string
}
export type NodeModalFieldType = {
address:string
address: string
}
export type NodeModalHandle = {
save:()=>void
save: () => void
}
export type NodeModalPropsType = {
changeStatus:(status:ClusterPageShowStatus)=>void
getClusterInfo:()=>void
status:ClusterPageShowStatus
changeStatus: (status: ClusterPageShowStatus) => void
getClusterInfo: () => void
status: ClusterPageShowStatus
}
export type ClusterPageShowStatus = 'view'|'preview'|'edit'
export type ClusterPageShowStatus = 'view' | 'preview' | 'edit'
export type PartitionTableListItem = {
id:string;
name: string;
clusterNum:number;
updater:EntityItem;
updateTime:string;
id: string;
name: string;
clusterNum: number;
updater: EntityItem;
updateTime: string;
};
export type SimplePartition = EntityItem & { clusters: (EntityItem & {description:string})[] }
export type SimplePartition = EntityItem & { clusters: (EntityItem & { description: string })[] }
export type PartitionDashboardConfigFieldType = {
driver:string
config:{
org:string
token:string
addr:string
driver: string
config: {
org: string
token: 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()
}, [serviceId]);
return (<WithPermission access="team.service.subscription.add">
return (<WithPermission access="">
<Form
layout='vertical'
labelAlign='left'
@@ -106,9 +106,9 @@ const AiSettingList = ()=>{
</a>
<div>
<CancelBtn/>
<WithPermission access="system.devops.ai_provider.edit" showDisabled={false}>
<OkBtn/>
</WithPermission>
{
checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn/> : null
}
</div>
</div>
);
@@ -3,7 +3,7 @@ import { useEffect } from "react";
import { PartitionDashboardConfigFieldType } from "../../const/partitions/types";
import { DASHBOARD_SETTING_DRIVER_OPTION_LIST } from "../../const/partitions/const";
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 { $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=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功,即将刷新页面')
message.success(msg || $t('操作成功,即将刷新页面'))
refreshData?.()
}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
layout='vertical'
labelAlign='left'
@@ -187,7 +187,7 @@ const PartitionInsideCert:FC = ()=>{
switch (type){
case 'add':
title=$t('添加证书')
content= <CertConfigModal ref={addRef} type="add"/>
content= <WithPermission access='system.devops.ssl_certificate.add'><CertConfigModal ref={addRef} type="add"/></WithPermission>
break;
case 'edit':{
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}})
message.destroy()
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{
message.error(msg || $t(RESPONSE_TIPS.error))
return
@@ -1,93 +1,175 @@
import { FC, useEffect, useState} from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {App, Button, Card, Col, Row, Spin, Tag} from "antd";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import { FC, useEffect, useState } from "react";
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext.tsx";
import { App, Button, Card, Col, Row, Spin, Tag } from "antd";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
import { useFetch } from "@common/hooks/http.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { LoadingOutlined } from "@ant-design/icons";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
import { $t } from "@common/locales/index.ts";
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 {setBreadcrumb} = useBreadcrumb()
const {message} = App.useApp()
const {fetchData} = useFetch()
const [data, setData] = useState<PartitionDashboardConfigFieldType>()
const [loading, setLoading] = useState<boolean>(false)
const [showStatus, setShowStatus] = useState<DashboardPageShowStatus>('view')
const PartitionInsideDashboardSetting: FC = () => {
const { setBreadcrumb } = useBreadcrumb()
const { message } = App.useApp()
const { fetchData } = useFetch()
const [data, setData] = useState<PartitionDashboardConfigFieldType>()
const [dataLogData, setDataLogData] = useState<PartitionDataLogConfigFieldType>()
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 = () => {
setLoading(true)
return fetchData<BasicResponse<{ nodes:PartitionDashboardConfigFieldType[] }>>('monitor/config', {method: 'GET',eoTransformKeys:[]}).then(response => {
const {code, data, msg} = response
if (code === STATUS_CODE.SUCCESS) {
data?.info?.driver && setData(data.info)
setShowStatus('view')
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
}).catch(() => {
return {data: [], success: false}
}).finally(()=>{
setLoading(false)
const getDashboardSettingInfo = () => {
setLoading(true)
return fetchData<BasicResponse<{ nodes: PartitionDashboardConfigFieldType[] }>>('monitor/config', { method: 'GET', eoTransformKeys: [] }).then(response => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
data?.info?.driver && setData(data.info)
setShowGraphStatus('view')
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
}).catch(() => {
return { data: [], success: false }
}).finally(() => {
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(() => {
setBreadcrumb([
{title: $t('数据源')}
])
getDashboardSettingInfo()
}, []);
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 setDashboardSettingBtn = () => {
return (<>
{showGraphStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
<Button type="primary" onClick={() => setShowGraphStatus('edit')}>{$t('修改配置')}</Button>
</WithPermission>}</>
)
}
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){
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('地址(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>
</div>}
export function DashboardConfigPreview(x: PartitionDashboardConfigFieldType) {
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('地址(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>
</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
@@ -79,7 +79,7 @@ const DataMasking = (props: any) => {
{
title: '',
key: 'option',
btnNums: rowOperation.length -1,
btnNums: rowOperation.length,
fixed: 'right',
valueType: 'option',
render: (_: React.ReactNode, entity: any) => [
@@ -233,7 +233,6 @@ const DataMasking = (props: any) => {
* @param entity
*/
const openLogsModal = (entity: any) => {
console.log('日志', entity);
setStrategy(entity.id)
setModalVisible(true)
}
@@ -379,10 +378,11 @@ const DataMasking = (props: any) => {
<Modal
title={$t('处理日志')}
visible={modalVisible}
destroyOnClose={true}
onCancel={handleCloseModal}
footer={null}
wrapClassName="modal-without-footer"
width={1000}
width={1100}
maskClosable={true}
>
<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,
onFilter: false ,
ellipsis: true,
width: 140,
width: 110,
valueEnum: new Map(
Object.keys(StrategyStatusEnum).map(key=>
[key,
@@ -40,6 +40,7 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
filters: true,
onFilter: false ,
ellipsis: true,
width: 90,
valueEnum: {
false: { text: <span className="text-status_success">{$t('启用')}</span> },
true: { text: <span className="text-status_fail">{$t('禁用')}</span> }
@@ -53,7 +54,8 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
{
title: ('处理数'),
dataIndex: 'processedTotal',
ellipsis: true
ellipsis: true,
width: 80,
},
{
title: ('更新者'),
@@ -83,10 +85,10 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
width: 200
},
{
title: ('消费者IP'),
title: ('消费者 IP'),
dataIndex: 'remote_ip',
ellipsis: true,
width: 150
width: 100
},
{
title: ('消费者'),
@@ -96,14 +98,14 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
},
{
title: ('鉴权名称'),
dataIndex: 'authorization',
dataIndex: ['authorization', 'name'],
ellipsis: true,
width: 100
},
{
title: ('时间'),
dataIndex: 'record_time',
width: 150,
width: 110,
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 { RouterParams } from "@common/const/type";
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 { useEffect, useState } from "react";
import { LoadingOutlined } from "@ant-design/icons";
import { useParams } from "react-router-dom";
import { contentTypeToLanguageMap } from "@common/const/policy/consts";
type LogItems = {
id: string;
origin: string;
@@ -18,7 +17,15 @@ const DataMaskingCompare = () => {
const { fetchData } = useFetch()
const [loading, setLoading] = useState(false)
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 = () => {
setLoading(true)
return fetchData<BasicResponse<{ log: LogItems }>>(`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/log`,
@@ -33,8 +40,10 @@ const DataMaskingCompare = () => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
const { log } = data
setOriginValue(log.origin || '')
settTargetValue(log.target || '')
const docLanguage = getMonacoEditorLanguage(log.content_type)
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)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
@@ -42,17 +51,6 @@ const DataMaskingCompare = () => {
}).catch(() => {
return { data: [], success: false }
}).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)
})
}
@@ -64,24 +62,26 @@ const DataMaskingCompare = () => {
<div className="flex h-full overflow-hidden">
<div className="w-1/2 p-2 h-full">
<div className="h-[30px] bg-gray-200 mb-2 flex items-center justify-center">
{$t('脱敏前')}
</div>
<div style={{ height: 'calc(100vh - 50px)' }}>
<Codebox
language='json'
language={language}
height="100%"
width="100%"
value={originValue}
sx={{ whiteSpace: 'nowrap' }}
readOnly
/>
</div>
</div>
<div className="w-1/2 p-2 h-full">
<div className="h-[30px] bg-green-100 mb-2 flex items-center justify-center">
{$t('脱敏后')}
</div>
<div style={{ height: 'calc(100vh - 50px)' }}>
<Codebox
language='json'
language={language}
width="100%"
height="100%"
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 PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
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 { ActionType } from '@ant-design/pro-components';
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const';
import { useParams } from 'react-router-dom';
import { RouterParams } from '@common/const/type';
import { useFetch } from '@common/hooks/http';
import WithPermission from '@common/components/aoplatform/WithPermission';
import TimeRangeSelector, { TimeRange } from '@common/components/aoplatform/TimeRangeSelector';
import { SearchBody } from '@dashboard/const/type';
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission';
const { RangePicker } = DatePicker;
const DataMaskingLogModal = (props: any) => {
const { strategy } = props;
const { state, accessData } = useGlobalContext()
const { serviceId, teamId } = useParams<RouterParams>()
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()
/**
* 列表ref
*/
* 列表ref
*/
const pageListRef = useRef<ActionType>(null);
/**
* 搜索关键字
*/
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>[] = [
{
title: '操作',
title: '',
key: 'option',
btnNums: 1,
fixed: 'right',
@@ -54,7 +70,7 @@ const DataMaskingLogModal = (props: any) => {
url += `/${teamId}`
}
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 res = DATA_MASKING_TABLE_LOG_COLUMNS.map(x => {
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 {
...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
@@ -88,7 +104,7 @@ const DataMaskingLogModal = (props: any) => {
current: 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',
eoParams: {
@@ -106,204 +122,9 @@ const DataMaskingLogModal = (props: any) => {
).then(response => {
const { code, data, msg } = response
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 {
data: mockData,
data: data.logs || [],
total: data.total,
success: true
}
@@ -320,24 +141,13 @@ const DataMaskingLogModal = (props: any) => {
const handleTimeRangeChange = (timeRange: TimeRange) => {
setQueryData(pre => ({ ...pre, ...timeRange } as SearchBody))
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 = () => {
setDatePickerValue(null)
handleTimeRangeChange({ start: null, end: null})
resetTimeRange()
};
return (
<>
<div className="w-full h-full p-[20px]">
@@ -348,11 +158,17 @@ const DataMaskingLogModal = (props: any) => {
columns={[...columns, ...operation]}
afterNewBtn={
[<div className="flex items-center flex-wrap p-[10px] px-btnbase content-before bg-MAIN_BG ">
<RangePicker
onChange={handleDatePickerChange}
value={datePickerValue} />
<div className="flex [&>.reset-btn]:!h-auto flex-nowrap items-center ml-[10px]">
<Button className="reset-btn" onClick={resetQuery}>{$t('重置')}</Button>
<TimeRangeSelector
labelSize="small"
bindRef={bindRef}
hideBtns={['hour']}
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>]
}
@@ -239,7 +239,7 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
getSystemList()
}, [serviceId]);
return (<WithPermission access="team.service.subscription.add">
return (<WithPermission access="">
<Form
layout='vertical'
labelAlign='left'
+2 -1
View File
@@ -77,7 +77,8 @@ export default defineConfig({
target: 'http://172.18.166.219:8288/',
changeOrigin: true,
}
}
},
open: true
},
logLevel:'info'
})
@@ -11,7 +11,7 @@ import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
import { SimpleTeamItem } from "@common/const/type";
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext";
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 WithPermission from "@common/components/aoplatform/WithPermission";
import InsidePage from "@common/components/aoplatform/InsidePage";
@@ -149,7 +149,7 @@ export default function ServiceHubManagement() {
switch (type){
case 'add':
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;
// case 'edit':{
// title='配置 Open Api'