mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d36c66371f | |||
| b7bb409e96 | |||
| 517007c941 | |||
| 4c685a9ec6 | |||
| 1aca2099de | |||
| a93e5b4ff8 | |||
| 85d25bebe2 | |||
| 9fa43ccc00 | |||
| c2a11050dd | |||
| 080bfc3a44 | |||
| f6956ddeca | |||
| 9f56fa5e14 | |||
| ccc39b95de | |||
| 9a2782e54b | |||
| 22455e2301 | |||
| 8ed2c84b68 | |||
| ccd2a209e2 | |||
| baf8ed4830 | |||
| dedb586daf | |||
| 21cd823791 | |||
| f1c16fd992 | |||
| 52035341f6 | |||
| aa62d44717 | |||
| a072d1fc8d | |||
| 38a00570d0 | |||
| 43283b9da3 | |||
| 3eb4f98fd8 | |||
| b8308a446b | |||
| 952c519e45 | |||
| 07d97fa0bf | |||
| b0defedf04 | |||
| a75b8a3f13 | |||
| 9ab7989c8b | |||
| 4bae2edc49 | |||
| e01f596525 | |||
| 570c80af91 | |||
| 836c7699b8 |
@@ -3,3 +3,4 @@
|
|||||||
/config.yml
|
/config.yml
|
||||||
/build/
|
/build/
|
||||||
/apipark
|
/apipark
|
||||||
|
.gitlab-ci.yml
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
variables:
|
|
||||||
PATH: /opt/go-1.21/go/bin/:/opt/node/node/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
|
|
||||||
GOROOT: /opt/go-1.21/go
|
|
||||||
GOPROXY: https://goproxy.cn
|
|
||||||
VERSION: $CI_COMMIT_SHORT_SHA
|
|
||||||
APP: apipark
|
|
||||||
APP_PRE: ${APP}_${VERSION}
|
|
||||||
BUILD_DIR: ${APP}-build
|
|
||||||
DEPLOY_DESC: "DEV 环境"
|
|
||||||
VIEW_ADDR: http://172.18.166.219:8288
|
|
||||||
SAVE_DIR: /opt/${APP}
|
|
||||||
NODE_OPTIONS: --max_old_space_size=8192
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- notice
|
|
||||||
- prefix
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
- webhook
|
|
||||||
|
|
||||||
feishu-informer: # 飞书回调
|
|
||||||
stage: notice
|
|
||||||
variables:
|
|
||||||
DIFF_URL: "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID/diffs"
|
|
||||||
rules:
|
|
||||||
- if: $CI_PIPELINE_SOURCE=="merge_request_event" && $CI_COMMIT_BRANCH =~ "main"
|
|
||||||
script:
|
|
||||||
- echo "merge request"
|
|
||||||
- |
|
|
||||||
curl -X POST -H "Content-Type: application/json" \
|
|
||||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"项目:${CI_PROJECT_NAME}\\n提交人:${GITLAB_USER_NAME}\\n提交信息:${CI_MERGE_REQUEST_TITLE}\\n合并分支信息:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}\\n差异性地址:${DIFF_URL}\\n请及时review代码\"}}" \
|
|
||||||
https://open.feishu.cn/open-apis/bot/v2/hook/1c334752-2874-41a1-8f1b-3060f2d46b6c
|
|
||||||
|
|
||||||
prebuild:
|
|
||||||
stage: prefix
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main"
|
|
||||||
script:
|
|
||||||
- echo "prebuild"
|
|
||||||
- chmod +x ./scripts/prefix.sh
|
|
||||||
- ./scripts/prefix.sh
|
|
||||||
|
|
||||||
builder:
|
|
||||||
stage: build
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main"
|
|
||||||
script:
|
|
||||||
- set -e
|
|
||||||
- |
|
|
||||||
if [ ! -d "../artifacts" ]; then
|
|
||||||
mkdir -p ../artifacts
|
|
||||||
fi
|
|
||||||
if [ -d "../artifacts/dist" ]; then
|
|
||||||
cp -r ../artifacts/dist frontend/dist
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [ -n "$(git diff --name-status HEAD~1 HEAD -- frontend)" ]; then
|
|
||||||
./scripts/build.sh $BUILD_DIR ${VERSION} all ""
|
|
||||||
else
|
|
||||||
./scripts/build.sh $BUILD_DIR ${VERSION}
|
|
||||||
fi
|
|
||||||
if [ -d "frontend/dist" ]; then
|
|
||||||
echo "copy frontend/dist to artifacts/dist"
|
|
||||||
rm -fr ../artifacts/dist
|
|
||||||
cp -r frontend/dist ../artifacts/dist
|
|
||||||
fi
|
|
||||||
cp $BUILD_DIR/${APP_PRE}_linux_amd64.tar.gz ${SAVE_DIR}
|
|
||||||
|
|
||||||
deployer:
|
|
||||||
stage: deploy
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main"
|
|
||||||
variables:
|
|
||||||
APIPARK_GUEST_MODE: allow
|
|
||||||
APIPARK_GUEST_ID: dklejrfbhjqwdh
|
|
||||||
script:
|
|
||||||
- cd ${SAVE_DIR};mkdir -p ${APP_PRE};tar -zxvf ${APP_PRE}_linux_amd64.tar.gz -C ${APP_PRE};cd ${APP_PRE};./install.sh ${SAVE_DIR};./run.sh restart;cd ${SAVE_DIR} && ./clean.sh ${APP_PRE}
|
|
||||||
when: on_success
|
|
||||||
success:
|
|
||||||
stage: webhook
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main"
|
|
||||||
script:
|
|
||||||
- |
|
|
||||||
curl -X POST -H "Content-Type: application/json" \
|
|
||||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署完成.\\n访问地址:${VIEW_ADDR}\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
|
|
||||||
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
|
|
||||||
when: on_success
|
|
||||||
failure:
|
|
||||||
stage: webhook
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main"
|
|
||||||
script:
|
|
||||||
- |
|
|
||||||
curl -X POST -H "Content-Type: application/json" \
|
|
||||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署失败,请及时到gitlab上查看\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
|
|
||||||
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
|
|
||||||
when: on_failure
|
|
||||||
@@ -27,7 +27,7 @@ type imlAPIController struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) {
|
func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) {
|
||||||
info, err := i.serviceModule.Get(ctx, serviceId)
|
_, err := i.serviceModule.Get(ctx, serviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
|||||||
plugins["ai_formatter"] = api.PluginSetting{
|
plugins["ai_formatter"] = api.PluginSetting{
|
||||||
Config: plugin_model.ConfigType{
|
Config: plugin_model.ConfigType{
|
||||||
"model": input.AiModel.Id,
|
"model": input.AiModel.Id,
|
||||||
"provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id),
|
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
|
||||||
"config": input.AiModel.Config,
|
"config": input.AiModel.Config,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
|||||||
Retry: input.Retry,
|
Retry: input.Retry,
|
||||||
Plugins: plugins,
|
Plugins: plugins,
|
||||||
},
|
},
|
||||||
Upstream: info.Provider.Id,
|
Upstream: input.AiModel.Provider,
|
||||||
Disable: false,
|
Disable: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) {
|
func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) {
|
||||||
info, err := i.serviceModule.Get(ctx, serviceId)
|
_, err := i.serviceModule.Get(ctx, serviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -106,11 +106,11 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
|
|||||||
proxy.Plugins["ai_formatter"] = api.PluginSetting{
|
proxy.Plugins["ai_formatter"] = api.PluginSetting{
|
||||||
Config: plugin_model.ConfigType{
|
Config: plugin_model.ConfigType{
|
||||||
"model": input.AiModel.Id,
|
"model": input.AiModel.Id,
|
||||||
"provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id),
|
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
|
||||||
"config": input.AiModel.Config,
|
"config": input.AiModel.Config,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
upstream = &info.Provider.Id
|
upstream = &input.AiModel.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.AiPrompt != nil {
|
if input.AiPrompt != nil {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ export interface IconParkIconElement extends HTMLElement {
|
|||||||
| 'apispace'
|
| 'apispace'
|
||||||
| 'auto-generate-api'
|
| 'auto-generate-api'
|
||||||
| 'compare-api'
|
| 'compare-api'
|
||||||
| 'multi-protocal'
|
| 'multi-protocol'
|
||||||
| 'read-good'
|
| 'read-good'
|
||||||
| 'richdoc'
|
| 'richdoc'
|
||||||
| 'mockapi'
|
| 'mockapi'
|
||||||
|
|||||||
@@ -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: '数据格式' },
|
||||||
@@ -55,3 +56,33 @@ export const StrategyStatusEnum = {
|
|||||||
"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> <span>{text}</span></>
|
x.render = (text: any, record: any) => <><div className='w-full'><span className='text-green-500'>{record.method}</span> <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
|
||||||
}
|
}
|
||||||
@@ -322,22 +143,11 @@ const DataMaskingLogModal = (props: any) => {
|
|||||||
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'
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ func NewDynamicClient(client admin_client.Client, resource string) (*DynamicClie
|
|||||||
cfg, has := gateway.GetDynamicResourceDriver(resource)
|
cfg, has := gateway.GetDynamicResourceDriver(resource)
|
||||||
if !has {
|
if !has {
|
||||||
return nil, errors.New("resource not found")
|
return nil, errors.New("resource not found")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DynamicClient{client: client, profession: cfg.Profession, driver: cfg.Driver}, nil
|
return &DynamicClient{client: client, profession: cfg.Profession, driver: cfg.Driver}, nil
|
||||||
|
|||||||
+4
-36
@@ -57,42 +57,10 @@ var dynamicResourceMap = map[string]Worker{
|
|||||||
Profession: ProfessionCertificate,
|
Profession: ProfessionCertificate,
|
||||||
Driver: "server",
|
Driver: "server",
|
||||||
},
|
},
|
||||||
//"openai": {
|
"loki": {
|
||||||
// Profession: ProfessionAIProvider,
|
Profession: ProfessionOutput,
|
||||||
// Driver: "openai",
|
Driver: "loki",
|
||||||
//},
|
},
|
||||||
//"google": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "google",
|
|
||||||
//},
|
|
||||||
//"anthropic": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "anthropic",
|
|
||||||
//},
|
|
||||||
//"moonshot": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "moonshot",
|
|
||||||
//},
|
|
||||||
//"tongyi": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "tongyi",
|
|
||||||
//},
|
|
||||||
//"zhipuai": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "zhipuai",
|
|
||||||
//},
|
|
||||||
//"fireworks": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "fireworks",
|
|
||||||
//},
|
|
||||||
//"novita": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "novita",
|
|
||||||
//},
|
|
||||||
//"mistralai": {
|
|
||||||
// Profession: ProfessionAIProvider,
|
|
||||||
// Driver: "mistralai",
|
|
||||||
//},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Worker struct {
|
type Worker struct {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type IFactory interface {
|
type IFactory interface {
|
||||||
Create(config string) (ILogDriver, error)
|
Create(config string) (ILogDriver, map[string]interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type factoryManager struct {
|
type factoryManager struct {
|
||||||
|
|||||||
+21
-7
@@ -11,6 +11,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/eolinker/eosc/log"
|
||||||
|
|
||||||
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ func init() {
|
|||||||
type factory struct {
|
type factory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *factory) Create(config string) (log_driver.ILogDriver, error) {
|
func (f *factory) Create(config string) (log_driver.ILogDriver, map[string]interface{}, error) {
|
||||||
|
|
||||||
return NewDriver(config)
|
return NewDriver(config)
|
||||||
}
|
}
|
||||||
@@ -35,24 +37,27 @@ type Driver struct {
|
|||||||
headers map[string]string
|
headers map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDriver(config string) (*Driver, error) {
|
func NewDriver(config string) (*Driver, map[string]interface{}, error) {
|
||||||
cfg := new(DriverConfig)
|
cfg := new(DriverConfig)
|
||||||
err := json.Unmarshal([]byte(config), cfg)
|
err := json.Unmarshal([]byte(config), cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
err = cfg.Check()
|
err = cfg.Check()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
headers := map[string]string{}
|
headers := map[string]string{}
|
||||||
for _, h := range cfg.Header {
|
for _, h := range cfg.Header {
|
||||||
headers[h.Key] = h.Value
|
headers[h.Key] = h.Value
|
||||||
}
|
}
|
||||||
return &Driver{
|
return &Driver{
|
||||||
url: cfg.URL,
|
url: cfg.URL,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
}, nil
|
}, map[string]interface{}{
|
||||||
|
"url": cfg.URL,
|
||||||
|
"headers": headers,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) LogInfo(clusterId string, id string) (*log_driver.LogInfo, error) {
|
func (d *Driver) LogInfo(clusterId string, id string) (*log_driver.LogInfo, error) {
|
||||||
@@ -66,6 +71,8 @@ func (d *Driver) LogInfo(clusterId string, id string) (*log_driver.LogInfo, erro
|
|||||||
queries.Set("start", strconv.FormatInt(start.UnixNano(), 10))
|
queries.Set("start", strconv.FormatInt(start.UnixNano(), 10))
|
||||||
queries.Set("end", strconv.FormatInt(now.UnixNano(), 10))
|
queries.Set("end", strconv.FormatInt(now.UnixNano(), 10))
|
||||||
queries.Set("limit", "1")
|
queries.Set("limit", "1")
|
||||||
|
log.Debug("query is ", queries.Get("query"))
|
||||||
|
|
||||||
list, err := send[LogInfo](http.MethodGet, fmt.Sprintf("%s/loki/api/v1/query_range", d.url), d.headers, queries, "")
|
list, err := send[LogInfo](http.MethodGet, fmt.Sprintf("%s/loki/api/v1/query_range", d.url), d.headers, queries, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -100,10 +107,13 @@ func (d *Driver) LogCount(clusterId string, conditions map[string]string, spendH
|
|||||||
}
|
}
|
||||||
queries := url.Values{}
|
queries := url.Values{}
|
||||||
queries.Set("query", fmt.Sprintf("sum(count_over_time({cluster=\"%s\"} | json %s [%dh])) by (%s)", clusterId, tmpCondition, spendHour, group))
|
queries.Set("query", fmt.Sprintf("sum(count_over_time({cluster=\"%s\"} | json %s [%dh])) by (%s)", clusterId, tmpCondition, spendHour, group))
|
||||||
|
sendRequestTime := time.Now()
|
||||||
list, err := send[LogCount](http.MethodGet, fmt.Sprintf("%s/loki/api/v1/query", d.url), d.headers, queries, "")
|
list, err := send[LogCount](http.MethodGet, fmt.Sprintf("%s/loki/api/v1/query", d.url), d.headers, queries, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
log.DebugF("send request spend time: %v", time.Now().Sub(sendRequestTime))
|
||||||
|
log.Debug("query is ", queries.Get("query"))
|
||||||
result := make(map[string]int64)
|
result := make(map[string]int64)
|
||||||
for _, l := range list {
|
for _, l := range list {
|
||||||
if len(l.Value) != 2 {
|
if len(l.Value) != 2 {
|
||||||
@@ -158,6 +168,7 @@ func (d *Driver) Logs(clusterId string, conditions map[string]string, start time
|
|||||||
queries.Set("limit", strconv.FormatInt(limit, 10))
|
queries.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
queries.Set("direction", "backward")
|
queries.Set("direction", "backward")
|
||||||
queries.Set("start", strconv.FormatInt(start.UnixNano(), 10))
|
queries.Set("start", strconv.FormatInt(start.UnixNano(), 10))
|
||||||
|
log.Debug("query is ", queries.Get("query"))
|
||||||
logs, err := d.recuseLogs(queries, end, offset)
|
logs, err := d.recuseLogs(queries, end, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -259,10 +270,13 @@ func send[T any](method string, uri string, headers map[string]string, queries u
|
|||||||
for key, value := range headers {
|
for key, value := range headers {
|
||||||
req.Header.Set(key, value)
|
req.Header.Set(key, value)
|
||||||
}
|
}
|
||||||
|
log.DebugF("do request: %s", uri)
|
||||||
|
doRequestTime := time.Now()
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to send request: %w", err)
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
||||||
}
|
}
|
||||||
|
log.DebugF("do request spend time: %v", time.Now().Sub(doRequestTime))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
respData, err := io.ReadAll(resp.Body)
|
respData, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ func newAIUpstream(provider string, uri model_runtime.IProviderURI) *gateway.Dyn
|
|||||||
"driver": "http",
|
"driver": "http",
|
||||||
"balance": "round-robin",
|
"balance": "round-robin",
|
||||||
"nodes": []string{fmt.Sprintf("%s weight=100", uri.Host())},
|
"nodes": []string{fmt.Sprintf("%s weight=100", uri.Host())},
|
||||||
"pass_node": "node",
|
"pass_host": "node",
|
||||||
"scheme": uri.Scheme(),
|
"scheme": uri.Scheme(),
|
||||||
"timeout": 300000,
|
"timeout": 300000,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Config) ID() string {
|
func (a *Config) ID() string {
|
||||||
//TODO implement me
|
return a.Ak
|
||||||
panic("implement me")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Config) Valid() ([]byte, error) {
|
func (a *Config) Valid() ([]byte, error) {
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []s
|
|||||||
Expire: a.ExpireTime,
|
Expire: a.ExpireTime,
|
||||||
Config: authCfg,
|
Config: authCfg,
|
||||||
HideCredential: a.HideCredential,
|
HideCredential: a.HideCredential,
|
||||||
|
Label: map[string]string{
|
||||||
|
"authorization": a.UUID,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
+167
-5
@@ -4,6 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||||
|
|
||||||
|
"github.com/APIParkLab/APIPark/gateway"
|
||||||
|
|
||||||
|
"github.com/eolinker/go-common/store"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
@@ -13,15 +20,64 @@ import (
|
|||||||
|
|
||||||
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
||||||
"github.com/APIParkLab/APIPark/service/log"
|
"github.com/APIParkLab/APIPark/service/log"
|
||||||
|
log_print "github.com/eolinker/eosc/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ ILogModule = (*imlLogModule)(nil)
|
var _ ILogModule = (*imlLogModule)(nil)
|
||||||
|
|
||||||
type imlLogModule struct {
|
type imlLogModule struct {
|
||||||
service log.ILogService `autowired:""`
|
service log.ILogService `autowired:""`
|
||||||
|
clusterService cluster.IClusterService `autowired:""`
|
||||||
|
transaction store.ITransaction `autowired:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels = map[string]string{
|
||||||
|
"cluster": "$cluster",
|
||||||
|
"node": "$node",
|
||||||
|
}
|
||||||
|
var logFormatter = map[string]interface{}{
|
||||||
|
"fields": []string{
|
||||||
|
"$msec",
|
||||||
|
"$service",
|
||||||
|
"$provider",
|
||||||
|
"$scheme as request_scheme",
|
||||||
|
"$url as request_uri",
|
||||||
|
"$host as request_host",
|
||||||
|
"$header as request_header",
|
||||||
|
"$remote_addr",
|
||||||
|
"$request_body",
|
||||||
|
"$proxy_body",
|
||||||
|
"$proxy_method",
|
||||||
|
"$proxy_scheme",
|
||||||
|
"$proxy_uri",
|
||||||
|
"$api",
|
||||||
|
"$proxy_host",
|
||||||
|
"$proxy_header",
|
||||||
|
"$proxy_addr",
|
||||||
|
"$response_headers",
|
||||||
|
"$status",
|
||||||
|
"$content_type",
|
||||||
|
"$proxy_status",
|
||||||
|
"$request_time",
|
||||||
|
"$response_time",
|
||||||
|
"$node",
|
||||||
|
"$cluster",
|
||||||
|
"$application",
|
||||||
|
"$src_ip",
|
||||||
|
"$block_name as strategy",
|
||||||
|
"$request_id",
|
||||||
|
"$request_method",
|
||||||
|
"$authorization",
|
||||||
|
"$response_body",
|
||||||
|
"$proxy_response_body",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *imlLogModule) Save(ctx context.Context, driver string, input *log_dto.Save) error {
|
func (i *imlLogModule) Save(ctx context.Context, driver string, input *log_dto.Save) error {
|
||||||
|
factory, has := log_driver.GetFactory(driver)
|
||||||
|
if !has {
|
||||||
|
return errors.New("driver not found")
|
||||||
|
}
|
||||||
input.Cluster = cluster.DefaultClusterID
|
input.Cluster = cluster.DefaultClusterID
|
||||||
var cfg *string
|
var cfg *string
|
||||||
if input.Config != nil {
|
if input.Config != nil {
|
||||||
@@ -29,10 +85,57 @@ func (i *imlLogModule) Save(ctx context.Context, driver string, input *log_dto.S
|
|||||||
tmp := string(data)
|
tmp := string(data)
|
||||||
cfg = &tmp
|
cfg = &tmp
|
||||||
}
|
}
|
||||||
return i.service.UpdateLogSource(ctx, driver, &log.Save{
|
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
|
||||||
ID: input.ID,
|
err := i.service.UpdateLogSource(txCtx, driver, &log.Save{
|
||||||
Cluster: &input.Cluster,
|
ID: input.ID,
|
||||||
Config: cfg,
|
Cluster: &input.Cluster,
|
||||||
|
Config: cfg,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := i.service.GetLogSource(txCtx, driver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d, c, err := factory.Create(info.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := i.clusterService.GatewayClient(txCtx, input.Cluster)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close(txCtx)
|
||||||
|
dynamicClient, err := client.Dynamic(driver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
attr := make(map[string]interface{})
|
||||||
|
attr["driver"] = driver
|
||||||
|
attr["formatter"] = logFormatter
|
||||||
|
attr["labels"] = labels
|
||||||
|
attr["method"] = "POST"
|
||||||
|
attr["scopes"] = []string{"access_log"}
|
||||||
|
attr["type"] = "json"
|
||||||
|
for k, v := range c {
|
||||||
|
attr[k] = v
|
||||||
|
}
|
||||||
|
err = dynamicClient.Online(txCtx, &gateway.DynamicRelease{
|
||||||
|
BasicItem: &gateway.BasicItem{
|
||||||
|
ID: driver,
|
||||||
|
Description: "collect access log",
|
||||||
|
Version: time.Now().Format("20060102150405"),
|
||||||
|
Resource: gateway.ProfessionOutput,
|
||||||
|
},
|
||||||
|
Attr: attr,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log_driver.SetDriver(driver, d)
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,3 +163,62 @@ func (i *imlLogModule) Get(ctx context.Context, driver string) (*log_dto.LogSour
|
|||||||
UpdateAt: auto.TimeLabel(info.UpdateAt),
|
UpdateAt: auto.TimeLabel(info.UpdateAt),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *imlLogModule) OnComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *imlLogModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
|
||||||
|
drivers := log_driver.Drivers()
|
||||||
|
if len(drivers) < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, driver := range drivers {
|
||||||
|
factory, has := log_driver.GetFactory(driver)
|
||||||
|
if !has {
|
||||||
|
log_print.Errorf("driver %s not found", driver)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info, err := i.service.GetLogSource(ctx, driver)
|
||||||
|
if err != nil {
|
||||||
|
log_print.Errorf("get log source %s error: %s", driver, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d, c, err := factory.Create(info.Config)
|
||||||
|
if err != nil {
|
||||||
|
log_print.Errorf("create driver %s error: %s,config: %s", driver, err, info.Config)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log_driver.SetDriver(driver, d)
|
||||||
|
dynamicClient, err := clientDriver.Dynamic(driver)
|
||||||
|
if err != nil {
|
||||||
|
log_print.Errorf("get dynamic client %s error: %s", driver, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attr := make(map[string]interface{})
|
||||||
|
attr["driver"] = driver
|
||||||
|
attr["formatter"] = logFormatter
|
||||||
|
attr["labels"] = labels
|
||||||
|
attr["method"] = "POST"
|
||||||
|
for k, v := range c {
|
||||||
|
attr[k] = v
|
||||||
|
}
|
||||||
|
err = dynamicClient.Online(ctx, &gateway.DynamicRelease{
|
||||||
|
BasicItem: &gateway.BasicItem{
|
||||||
|
ID: driver,
|
||||||
|
Description: "collect access log",
|
||||||
|
Version: time.Now().Format("20060102150405"),
|
||||||
|
Resource: gateway.ProfessionOutput,
|
||||||
|
},
|
||||||
|
Attr: attr,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log_print.Errorf("online driver %s error: %s", driver, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/eolinker/go-common/autowire"
|
"github.com/eolinker/go-common/autowire"
|
||||||
|
|
||||||
|
"github.com/APIParkLab/APIPark/gateway"
|
||||||
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +16,11 @@ type ILogModule interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
logModule := new(imlLogModule)
|
||||||
|
|
||||||
autowire.Auto[ILogModule](func() reflect.Value {
|
autowire.Auto[ILogModule](func() reflect.Value {
|
||||||
return reflect.ValueOf(new(imlLogModule))
|
|
||||||
|
gateway.RegisterInitHandleFunc(logModule.initGateway)
|
||||||
|
return reflect.ValueOf(logModule)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,3 +359,36 @@ func (i *imlStrategyModule) Delete(ctx context.Context, id string) error {
|
|||||||
}
|
}
|
||||||
return i.strategyService.SortDelete(ctx, id)
|
return i.strategyService.SortDelete(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *imlStrategyModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
|
||||||
|
commits, err := i.strategyService.ListLatestStrategyCommit(ctx, strategy_dto.ScopeGlobal, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
publishStrategies := make([]*eosc.Base[gateway.StrategyRelease], 0, len(commits))
|
||||||
|
for _, c := range commits {
|
||||||
|
l := c.Data
|
||||||
|
if l.IsDelete {
|
||||||
|
err = i.strategyService.Delete(ctx, l.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d, has := strategy_driver.GetDriver(l.Driver)
|
||||||
|
if !has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
publishStrategies = append(publishStrategies, d.ToRelease(strategy_dto.ToStrategy(&strategy.Strategy{
|
||||||
|
Id: l.Id,
|
||||||
|
Name: l.Name,
|
||||||
|
Priority: l.Priority,
|
||||||
|
Filters: l.Filters,
|
||||||
|
Config: l.Config,
|
||||||
|
Driver: l.Driver,
|
||||||
|
IsStop: l.IsStop,
|
||||||
|
IsDelete: l.IsDelete,
|
||||||
|
}), nil, 5000))
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientDriver.Strategy().Online(ctx, publishStrategies...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/APIParkLab/APIPark/gateway"
|
||||||
|
|
||||||
"github.com/eolinker/go-common/autowire"
|
"github.com/eolinker/go-common/autowire"
|
||||||
|
|
||||||
_ "github.com/APIParkLab/APIPark/module/strategy/driver/data-masking"
|
_ "github.com/APIParkLab/APIPark/module/strategy/driver/data-masking"
|
||||||
@@ -32,6 +34,7 @@ type IStrategyModule interface {
|
|||||||
func init() {
|
func init() {
|
||||||
strategyModule := new(imlStrategyModule)
|
strategyModule := new(imlStrategyModule)
|
||||||
autowire.Auto[IStrategyModule](func() reflect.Value {
|
autowire.Auto[IStrategyModule](func() reflect.Value {
|
||||||
|
gateway.RegisterInitHandleFunc(strategyModule.initGateway)
|
||||||
return reflect.ValueOf(strategyModule)
|
return reflect.ValueOf(strategyModule)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ func (i *imlAPIService) Save(ctx context.Context, id string, model *Edit) error
|
|||||||
if model.Disable != nil {
|
if model.Disable != nil {
|
||||||
ev.Disable = *model.Disable
|
ev.Disable = *model.Disable
|
||||||
}
|
}
|
||||||
|
if model.Upstream != nil {
|
||||||
|
ev.Upstream = *model.Upstream
|
||||||
|
}
|
||||||
|
|
||||||
e := i.apiInfoStore.Save(ctx, ev)
|
e := i.apiInfoStore.Save(ctx, ev)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
|||||||
+16
-15
@@ -5,6 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log_print "github.com/eolinker/eosc/log"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||||
@@ -26,29 +28,31 @@ type imlLogService struct {
|
|||||||
|
|
||||||
func (i *imlLogService) OnComplete() {
|
func (i *imlLogService) OnComplete() {
|
||||||
drivers := log_driver.Drivers()
|
drivers := log_driver.Drivers()
|
||||||
for _, d := range drivers {
|
if len(drivers) < 1 {
|
||||||
factory, has := log_driver.GetFactory(d)
|
return
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, driver := range drivers {
|
||||||
|
factory, has := log_driver.GetFactory(driver)
|
||||||
if !has {
|
if !has {
|
||||||
|
log_print.Errorf("driver %s not found", driver)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
s, err := i.GetLogSource(context.Background(), d)
|
info, err := i.GetLogSource(ctx, driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log_print.Errorf("get log source %s error: %s", driver, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
driver, err := factory.Create(s.Config)
|
d, _, err := factory.Create(info.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log_print.Errorf("create driver %s error: %s,config: %s", driver, err, info.Config)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log_driver.SetDriver(d, driver)
|
log_driver.SetDriver(driver, d)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *imlLogService) UpdateLogSource(ctx context.Context, driver string, input *Save) error {
|
func (i *imlLogService) UpdateLogSource(ctx context.Context, driver string, input *Save) error {
|
||||||
factory, has := log_driver.GetFactory(driver)
|
|
||||||
if !has {
|
|
||||||
return errors.New("driver not found")
|
|
||||||
}
|
|
||||||
s, err := i.store.First(ctx, map[string]interface{}{"driver": driver})
|
s, err := i.store.First(ctx, map[string]interface{}{"driver": driver})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
@@ -83,15 +87,12 @@ func (i *imlLogService) UpdateLogSource(ctx context.Context, driver string, inpu
|
|||||||
s.Updater = utils.UserId(ctx)
|
s.Updater = utils.UserId(ctx)
|
||||||
s.UpdateAt = time.Now()
|
s.UpdateAt = time.Now()
|
||||||
}
|
}
|
||||||
newDriver, err := factory.Create(s.Config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = i.store.Save(ctx, s)
|
err = i.store.Save(ctx, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log_driver.SetDriver(driver, newDriver)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user