mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e449f86c01 | |||
| a1bdc048a7 | |||
| 0a1b08157d | |||
| 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 |
+2
-1
@@ -2,4 +2,5 @@
|
||||
/.idea/
|
||||
/config.yml
|
||||
/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) {
|
||||
info, err := i.serviceModule.Get(ctx, serviceId)
|
||||
_, err := i.serviceModule.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
||||
plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"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,
|
||||
},
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
||||
Retry: input.Retry,
|
||||
Plugins: plugins,
|
||||
},
|
||||
Upstream: info.Provider.Id,
|
||||
Upstream: input.AiModel.Provider,
|
||||
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) {
|
||||
info, err := i.serviceModule.Get(ctx, serviceId)
|
||||
_, err := i.serviceModule.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,11 +106,11 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
|
||||
proxy.Plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"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,
|
||||
},
|
||||
}
|
||||
upstream = &info.Provider.Id
|
||||
upstream = &input.AiModel.Provider
|
||||
}
|
||||
|
||||
if input.AiPrompt != nil {
|
||||
|
||||
@@ -326,7 +326,33 @@ func (i *imlServiceController) Create(ctx *gin.Context, teamID string, input *se
|
||||
if input.Kind == "ai" {
|
||||
return i.createAIService(ctx, teamID, input)
|
||||
}
|
||||
return i.module.Create(ctx, teamID, input)
|
||||
var err error
|
||||
var info *service_dto.Service
|
||||
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
|
||||
info, err = i.module.Create(txCtx, teamID, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := fmt.Sprintf("/%s/", strings.Trim(input.Prefix, "/"))
|
||||
_, err = i.routerModule.Create(txCtx, info.Id, &router_dto.Create{
|
||||
Id: uuid.New().String(),
|
||||
Name: "",
|
||||
Path: path + "*",
|
||||
Methods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
|
||||
Description: "auto create by create service",
|
||||
Protocols: []string{"http", "https"},
|
||||
MatchRules: nil,
|
||||
Upstream: "",
|
||||
Proxy: &router_dto.InputProxy{
|
||||
Path: path,
|
||||
Timeout: 30000,
|
||||
Retry: 0,
|
||||
},
|
||||
Disable: false,
|
||||
})
|
||||
return err
|
||||
})
|
||||
return info, err
|
||||
}
|
||||
|
||||
func (i *imlServiceController) Edit(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) {
|
||||
|
||||
@@ -265,7 +265,8 @@ func (i *imlInitController) OnInit() {
|
||||
return fmt.Errorf("create default team error: %v", err)
|
||||
}
|
||||
// 创建Rest服务
|
||||
_, err = i.serviceModule.Create(ctx, info.Id, &service_dto.CreateService{
|
||||
restPath := "/rest-demo"
|
||||
serviceInfo, err := i.serviceModule.Create(ctx, info.Id, &service_dto.CreateService{
|
||||
Name: "REST Demo Service",
|
||||
Prefix: "/rest-demo",
|
||||
Description: "Auto created By APIPark",
|
||||
@@ -277,6 +278,26 @@ func (i *imlInitController) OnInit() {
|
||||
if err != nil {
|
||||
return fmt.Errorf("create default service error: %v", err)
|
||||
}
|
||||
path := fmt.Sprintf("/%s/", strings.Trim(restPath, "/"))
|
||||
_, err = i.routerModule.Create(ctx, serviceInfo.Id, &router_dto.Create{
|
||||
Id: uuid.NewString(),
|
||||
Name: "",
|
||||
Path: path + "*",
|
||||
Methods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
|
||||
Description: "auto create by create service",
|
||||
Protocols: []string{"http", "https"},
|
||||
MatchRules: nil,
|
||||
Upstream: "",
|
||||
Proxy: &router_dto.InputProxy{
|
||||
Path: path,
|
||||
Timeout: 30000,
|
||||
Retry: 0,
|
||||
},
|
||||
Disable: false,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create default router error: %v", err)
|
||||
}
|
||||
// 创建AI服务
|
||||
err = i.createAIService(ctx, info.Id, &service_dto.CreateService{
|
||||
Name: "AI Demo Service",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"prettier/prettier": "error",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn"]
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 100,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"jsxSingleQuote": false
|
||||
}
|
||||
@@ -4,69 +4,88 @@ import { memo, useEffect, useMemo } from 'react';
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
|
||||
import { Icon } from '@iconify/react/dist/iconify.js';
|
||||
|
||||
const LanguageSetting = ({mode = 'light'}:{mode?:'dark'|'light'}) => {
|
||||
const { dispatch,state} = useGlobalContext()
|
||||
const items = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label:<Button key="en" type="text" className="border-none p-0 flex items-center bg-transparent ">
|
||||
English
|
||||
</Button>,
|
||||
title:'English'
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: <Button key="jp" type="text" className="border-none p-0 flex items-center bg-transparent ">
|
||||
const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
const { dispatch, state } = useGlobalContext();
|
||||
const items = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label: (
|
||||
<Button key="en" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
English
|
||||
</Button>
|
||||
),
|
||||
title: 'English',
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: (
|
||||
<Button key="jp" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
日本語
|
||||
</Button>,
|
||||
title: '日本語',
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: <Button key="tw" type="text" className="border-none p-0 flex items-center bg-transparent ">
|
||||
繁體中文
|
||||
</Button>,
|
||||
title: '繁體中文',
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: <Button key="cn" type="text" className="border-none p-0 flex items-center bg-transparent ">
|
||||
简体中文
|
||||
</Button>,
|
||||
title: '简体中文',
|
||||
},
|
||||
</Button>
|
||||
),
|
||||
title: '日本語',
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: (
|
||||
<Button key="tw" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
繁體中文
|
||||
</Button>
|
||||
),
|
||||
title: '繁體中文',
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: (
|
||||
<Button key="cn" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
简体中文
|
||||
</Button>
|
||||
),
|
||||
title: '简体中文',
|
||||
},
|
||||
];
|
||||
|
||||
const langLabel = useMemo(()=>items.find((item) => item?.key === state.language)?.title,[state.language])
|
||||
const langLabel = useMemo(
|
||||
() => items.find(item => item?.key === state.language)?.title,
|
||||
[state.language]
|
||||
);
|
||||
|
||||
useEffect(()=>{
|
||||
const savedLang = sessionStorage.getItem('i18nextLng')
|
||||
const browserLang = navigator.language || navigator.userLanguage
|
||||
if(savedLang){
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: savedLang });
|
||||
}else{
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: browserLang });
|
||||
}
|
||||
},[
|
||||
])
|
||||
useEffect(() => {
|
||||
const savedLang = sessionStorage.getItem('i18nextLng');
|
||||
const browserLang = navigator.language || navigator.userLanguage;
|
||||
if (savedLang) return;
|
||||
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: browserLang });
|
||||
}, []);
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['hover']}
|
||||
menu={{
|
||||
items,
|
||||
style:{minWidth:'80px'},
|
||||
onClick: (e) => {
|
||||
style: { minWidth: '80px' },
|
||||
onClick: e => {
|
||||
const { key } = e;
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: key });
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: key });
|
||||
i18n.changeLanguage(key);
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button className={`border-none ${mode==='dark' ? "text-[#333] hover:text-[#333333b3]" : "text-[#ffffffb3] hover:text-[#fff] "}`} type="default" ghost >
|
||||
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-language" width="14" height="14"/>{langLabel}</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={`border-none ${
|
||||
mode === 'dark'
|
||||
? 'text-[#333] hover:text-[#333333b3]'
|
||||
: 'text-[#ffffffb3] hover:text-[#fff] '
|
||||
}`}
|
||||
type="default"
|
||||
ghost
|
||||
>
|
||||
<span className="flex items-center gap-[8px]">
|
||||
{' '}
|
||||
<Icon icon="ic:baseline-language" width="14" height="14" />
|
||||
{langLabel}
|
||||
</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
export default memo(LanguageSetting);
|
||||
|
||||
|
||||
@@ -146,8 +146,11 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
|
||||
...x,
|
||||
title: typeof x.title === 'string' ? $t(x.title) : x.title,
|
||||
...(x.dataIndex === 'status' ? {
|
||||
render:(_,entity)=>(
|
||||
entity.status === 0 ? $t('正常') : $t('无效'))
|
||||
render:(_,entity)=> (
|
||||
<span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>
|
||||
{$t(ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-')}
|
||||
</span>
|
||||
)
|
||||
}:{})
|
||||
}
|
||||
}),[state.language])
|
||||
|
||||
@@ -37,6 +37,8 @@ const TableIconName = {
|
||||
const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateTo, onClick, className, btnType }: TableBtnWithPermissionProps) => {
|
||||
|
||||
const [btnAccess, setBtnAccess] = useState<boolean>(false)
|
||||
const [btnStatus, setBtnStatus] = useState<boolean>(false)
|
||||
const [closeToolTip, setCloseToolTip] = useState<boolean>(false)
|
||||
const { accessData, checkPermission, accessInit } = useGlobalContext()
|
||||
const navigate = useNavigate()
|
||||
const lastAccess = useMemo(() => {
|
||||
@@ -52,16 +54,27 @@ const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateT
|
||||
|
||||
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation()
|
||||
setTimeout(() => {
|
||||
setBtnStatus(false)
|
||||
setCloseToolTip(true)
|
||||
})
|
||||
|
||||
navigateTo ? navigate(navigateTo) : onClick?.()
|
||||
}, [navigateTo, navigate, onClick])
|
||||
|
||||
const changeTooltipStatus = (open: boolean) => {
|
||||
setBtnStatus(open)
|
||||
if (closeToolTip) {
|
||||
setBtnStatus(false)
|
||||
setCloseToolTip(false)
|
||||
}
|
||||
}
|
||||
return (<>{
|
||||
!btnAccess || (disabled && tooltip) ?
|
||||
<Tooltip placement="top" title={tooltip ?? $t('暂无(0)权限,请联系管理员分配。', [$t(btnTitle).toLowerCase()])}>
|
||||
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} >{ }</Button>
|
||||
</Tooltip>
|
||||
:
|
||||
<Tooltip placement="top" title={$t(btnTitle)}>
|
||||
<Tooltip placement="top" title={$t(btnTitle)} trigger='hover' open={btnStatus} onOpenChange={changeTooltipStatus}>
|
||||
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} onClick={handleClick}>{ }</Button>
|
||||
</Tooltip>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||
@@ -12,69 +12,80 @@ export type RangeValue = [Dayjs | null, Dayjs | null] | null;
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
export type TimeRange = {
|
||||
start:number|null
|
||||
end:number|null
|
||||
start: number | null
|
||||
end: number | null
|
||||
}
|
||||
|
||||
export type TimeRangeButton = ''| 'hour' | 'day' | 'threeDays' | 'sevenDays';
|
||||
export type TimeRangeButton = '' | 'hour' | 'day' | 'threeDays' | 'sevenDays';
|
||||
|
||||
type TimeRangeSelectorProps = {
|
||||
initialTimeButton?:TimeRangeButton,
|
||||
initialDatePickerValue?:RangeValue
|
||||
onTimeRangeChange?:(timeRange:TimeRange) =>void
|
||||
hideTitle?:boolean
|
||||
onTimeButtonChange:(time:TimeRangeButton) =>void
|
||||
labelSize?:'small'|'default'
|
||||
}
|
||||
const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
|
||||
const {initialTimeButton,initialDatePickerValue,onTimeRangeChange,hideTitle,onTimeButtonChange,labelSize='default'} = props
|
||||
initialTimeButton?: TimeRangeButton,
|
||||
initialDatePickerValue?: RangeValue
|
||||
onTimeRangeChange?: (timeRange: TimeRange) => void
|
||||
hideTitle?: boolean
|
||||
onTimeButtonChange: (time: TimeRangeButton) => void
|
||||
labelSize?: 'small' | 'default'
|
||||
bindRef?: any
|
||||
hideBtns?: TimeRangeButton[]
|
||||
defaultTimeButton?: TimeRangeButton
|
||||
}
|
||||
const TimeRangeSelector = (props: TimeRangeSelectorProps) => {
|
||||
const { initialTimeButton, initialDatePickerValue, onTimeRangeChange, hideTitle, onTimeButtonChange, labelSize = 'default', bindRef, hideBtns = [], defaultTimeButton = 'hour' } = props
|
||||
const [timeButton, setTimeButton] = useState(initialTimeButton || '');
|
||||
const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null,null]);
|
||||
|
||||
const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null, null]);
|
||||
useEffect(() => {
|
||||
if (bindRef) {
|
||||
bindRef({ reset });
|
||||
}
|
||||
}, [bindRef])
|
||||
// 根据选择的时间范围计算开始和结束时间
|
||||
const calculateTimeRange = (curBtn:'hour'|'day'|'threeDays'|'sevenDays') => {
|
||||
const currentSecond = new Date().getTime() // 当前毫秒数时间戳
|
||||
const currentMin = currentSecond - (currentSecond % (60 * 1000)) // 当前分钟数时间戳
|
||||
let startMin = currentMin - 60 * 60 * 1000
|
||||
const calculateTimeRange = (curBtn: TimeRangeButton) => {
|
||||
const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
|
||||
let startMin = currentSecond - 60 * 60
|
||||
switch (curBtn) {
|
||||
case 'hour': {
|
||||
startMin = currentMin - 60 * 60 * 1000
|
||||
break
|
||||
}
|
||||
case 'day': {
|
||||
startMin = currentMin - 24 * 60 * 60 * 1000
|
||||
break
|
||||
}
|
||||
case 'threeDays': {
|
||||
startMin =
|
||||
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
|
||||
2 * 24 * 60 * 60 * 1000
|
||||
break
|
||||
}
|
||||
case 'sevenDays': {
|
||||
startMin =
|
||||
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
|
||||
6 * 24 * 60 * 60 * 1000
|
||||
break
|
||||
}
|
||||
case 'hour': {
|
||||
startMin = currentSecond - 60 * 60
|
||||
break
|
||||
}
|
||||
case 'day': {
|
||||
startMin = currentSecond - 24 * 60 * 60
|
||||
break
|
||||
}
|
||||
case 'threeDays': {
|
||||
startMin =
|
||||
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
|
||||
2 * 24 * 60 * 60
|
||||
break
|
||||
}
|
||||
case 'sevenDays': {
|
||||
startMin =
|
||||
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
|
||||
6 * 24 * 60 * 60
|
||||
break
|
||||
}
|
||||
}
|
||||
if (onTimeRangeChange) {
|
||||
onTimeRangeChange({ start: startMin / 1000, end: currentMin / 1000 });
|
||||
onTimeRangeChange({ start: startMin, end: currentSecond });
|
||||
}
|
||||
};
|
||||
|
||||
// 处理单选按钮的变化
|
||||
const handleRadioChange = (e:RadioChangeEvent) => {
|
||||
const handleRadioChange = (e: RadioChangeEvent) => {
|
||||
setTimeButton(e.target.value);
|
||||
onTimeButtonChange?.(e.target.value)
|
||||
setDatePickerValue(null)
|
||||
calculateTimeRange(e.target.value);
|
||||
};
|
||||
const reset = () => {
|
||||
setTimeButton(defaultTimeButton)
|
||||
calculateTimeRange(defaultTimeButton)
|
||||
setDatePickerValue(null)
|
||||
}
|
||||
|
||||
// 处理日期选择器的变化
|
||||
const handleDatePickerChange = (dates: RangeValue) => {
|
||||
setTimeButton(dates ? '' : 'hour')
|
||||
onTimeButtonChange?.(dates ? '' : 'hour')
|
||||
setTimeButton(dates ? '' : defaultTimeButton)
|
||||
onTimeButtonChange?.(dates ? '' : defaultTimeButton)
|
||||
setDatePickerValue(dates);
|
||||
if (dates && Array.isArray(dates) && dates.length === 2) {
|
||||
const [startDate, endDate] = dates;
|
||||
@@ -84,34 +95,37 @@ const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
|
||||
onTimeRangeChange({ start, end });
|
||||
}
|
||||
}
|
||||
if (!dates) {
|
||||
calculateTimeRange(defaultTimeButton)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
||||
// Can not select days before today and today
|
||||
|
||||
|
||||
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
||||
// Can not select days before today and today
|
||||
return current && current.valueOf() > dayjs().startOf('day').valueOf();
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
|
||||
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}:</label>}
|
||||
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
|
||||
<Radio.Button value="hour">{$t('近1小时')}</Radio.Button>
|
||||
<Radio.Button value="day">{$t('近24小时')}</Radio.Button>
|
||||
<Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>
|
||||
<Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>
|
||||
</Radio.Group>
|
||||
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}:</label>}
|
||||
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
|
||||
{hideBtns?.length && hideBtns.includes('hour') ? null : <Radio.Button value="hour">{$t('近1小时')}</Radio.Button>}
|
||||
{hideBtns?.length && hideBtns.includes('day') ? null : <Radio.Button value="day">{$t('近24小时')}</Radio.Button>}
|
||||
{hideBtns?.length && hideBtns.includes('threeDays') ? null : <Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>}
|
||||
{hideBtns?.length && hideBtns.includes('sevenDays') ? null : <Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>}
|
||||
</Radio.Group>
|
||||
<DatePicker.RangePicker
|
||||
value={datePickerValue}
|
||||
className="rounded-s-none ml-[-1px]"
|
||||
className="rounded-s-none ml-[-1px]"
|
||||
disabledDate={disabledDate}
|
||||
onChange={handleDatePickerChange}
|
||||
onOpenChange={(open)=>{
|
||||
if(!open && datePickerValue && datePickerValue.length > 2){
|
||||
setTimeButton('')
|
||||
onTimeButtonChange?.('')
|
||||
}
|
||||
onOpenChange={(open) => {
|
||||
if (!open && datePickerValue && datePickerValue.length > 2) {
|
||||
setTimeButton('')
|
||||
onTimeButtonChange?.('')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -181,6 +181,10 @@ export const TranslateWord = ()=>{
|
||||
{$t('请输入IP地址或CIDR范围,每条以换行分割')}
|
||||
{$t('待更新')}
|
||||
{$t('待删除')}
|
||||
{$t('内容')}
|
||||
{$t('调用地址')}
|
||||
{$t('消费者 IP')}
|
||||
{$t('鉴权名称')}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export interface CodeboxApiRef {
|
||||
formatCode: () => void
|
||||
}
|
||||
|
||||
export type codeBoxLanguagesType = 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
|
||||
interface CodeboxProps {
|
||||
options?: MonacoEditor.IStandaloneEditorConstructionOptions
|
||||
value?: string
|
||||
@@ -22,7 +23,7 @@ interface CodeboxProps {
|
||||
height?: string | null
|
||||
readOnly?: boolean
|
||||
apiRef?: RefObject<CodeboxApiRef>
|
||||
language?: 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
|
||||
language?: codeBoxLanguagesType
|
||||
extraContent?:React.ReactNode
|
||||
sx?:Record<string,unknown>
|
||||
editorTheme?:'vs' | 'vs-dark' | 'hc-black'
|
||||
|
||||
@@ -295,7 +295,7 @@ export interface IconParkIconElement extends HTMLElement {
|
||||
| 'apispace'
|
||||
| 'auto-generate-api'
|
||||
| 'compare-api'
|
||||
| 'multi-protocal'
|
||||
| 'multi-protocol'
|
||||
| 'read-good'
|
||||
| 'richdoc'
|
||||
| 'mockapi'
|
||||
|
||||
@@ -486,17 +486,17 @@ export const PERMISSION_DEFINITION = [
|
||||
},
|
||||
"team.application.subscription.add": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.subscription.subscribe"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.subscribe"] }]
|
||||
}
|
||||
},
|
||||
"team.application.subscription.edit": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.subscription.manager_subscribed_services"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.manager_subscribed_services"] }]
|
||||
}
|
||||
},
|
||||
"team.application.subscription.delete": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.team.consumer.subscription.manager_subscribed_services"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.team.consumer.subscription.manager_subscribed_services"] }]
|
||||
}
|
||||
},
|
||||
"team.application.application.view": {
|
||||
@@ -506,47 +506,47 @@ export const PERMISSION_DEFINITION = [
|
||||
},
|
||||
"team.application.application.add": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.application.edit": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.application.delete": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.consumer.authorization.view": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","system.workspace.application.view_all","team.consumer.authorization.view"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","system.workspace.application.view_all","team.consumer.authorization.view"] }]
|
||||
}
|
||||
},
|
||||
"team.application.authorization.add": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.authorization.edit": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.authorization.delete": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.authorization.cancelSubApply": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.application.authorization.cancelSub": {
|
||||
"granted": {
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_al","team.consumer.authorization.manager"] }]
|
||||
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
|
||||
}
|
||||
},
|
||||
"team.team.team.view": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
|
||||
|
||||
export const MatchRules = [
|
||||
{ value: 'inner', label: '数据格式' },
|
||||
@@ -54,4 +55,34 @@ export const StrategyStatusEnum = {
|
||||
"offline":'text-status_fail',
|
||||
"delete":'text-status_offline',
|
||||
}
|
||||
|
||||
export const contentTypeToLanguageMap: Record<string, codeBoxLanguagesType> = {
|
||||
// JSON
|
||||
"application/json": "json",
|
||||
|
||||
// XML
|
||||
"application/xml": "xml",
|
||||
"text/xml": "xml",
|
||||
|
||||
// HTML
|
||||
"text/html": "html",
|
||||
|
||||
// Plain text
|
||||
"text/plain": "plaintext",
|
||||
|
||||
// JavaScript
|
||||
"application/javascript": "javascript",
|
||||
"text/javascript": "javascript",
|
||||
|
||||
// CSS
|
||||
"text/css": "css",
|
||||
|
||||
// YAML
|
||||
"application/x-yaml": "yaml",
|
||||
"text/yaml": "yaml",
|
||||
|
||||
// Others (fallback)
|
||||
"*/*": "plaintext", // 任意类型默认处理为普通文本
|
||||
};
|
||||
|
||||
|
||||
@@ -749,5 +749,19 @@
|
||||
"K67f4e9bb": "The domain name for obtaining API market documentation information when integrating with external platforms",
|
||||
"K1da86266": "Invalid",
|
||||
"K3a34d49b": "Pending Update",
|
||||
"Kd2850420": "Pending Deletion"
|
||||
"Kd2850420": "Pending Deletion",
|
||||
"K9ada4366": "Operation successful, the page will refresh shortly",
|
||||
"K9b332ab1": "Request prefix",
|
||||
"K3d78d483": "HTTP headers",
|
||||
"K17dc3a62": "Data logs",
|
||||
"Ke429194e": "Processing logs",
|
||||
"K84ffb1dd": "Enter the invocation address, consumer IP, and consumer condition to search",
|
||||
"Kb147fabc": "Create",
|
||||
"K40ca4f2": "Update",
|
||||
"K3e7aa0ad": "Content",
|
||||
"K2f5fdf5e": "Call Address",
|
||||
"K1bc5e0a3": "Consumer IP",
|
||||
"K6f39ea21": "Authentication Name",
|
||||
"K8c34c02f": "Before Masking",
|
||||
"K8e3d388d": "After Masking"
|
||||
}
|
||||
|
||||
@@ -771,6 +771,19 @@
|
||||
"K67f4e9bb": "外部プラットフォームと統合する際に、API市場のドキュメント情報を取得するためのドメイン名",
|
||||
"K1da86266": "無効",
|
||||
"K3a34d49b": "更新待ち",
|
||||
"Kd2850420": "削除待ち"
|
||||
|
||||
"Kd2850420": "削除待ち",
|
||||
"K9ada4366": "操作成功、ページを更新します",
|
||||
"K9b332ab1": "リクエストプレフィックス",
|
||||
"K3d78d483": "HTTPヘッダー",
|
||||
"K17dc3a62": "データログ",
|
||||
"Ke429194e": "ログの処理",
|
||||
"K84ffb1dd": "呼び出しアドレス、コンシューマーIP、条件を入力して検索",
|
||||
"Kb147fabc": "新規作成",
|
||||
"K40ca4f2": "更新",
|
||||
"K3e7aa0ad": "内容",
|
||||
"K2f5fdf5e": "呼び出しアドレス",
|
||||
"K1bc5e0a3": "コンシューマー IP",
|
||||
"K6f39ea21": "認証名",
|
||||
"K8c34c02f": "マスキング前",
|
||||
"K8e3d388d": "マスキング後"
|
||||
}
|
||||
|
||||
@@ -702,5 +702,19 @@
|
||||
"K31faa2a1": "优先级",
|
||||
"Kbdec9fa": "筛选条件",
|
||||
"Kbcbb7391": "处理数",
|
||||
"Kad207008": "编辑"
|
||||
"Kad207008": "编辑",
|
||||
"K9ada4366": "操作成功,即将刷新页面",
|
||||
"K9b332ab1": "请求前缀",
|
||||
"K3d78d483": "HTTP 头部",
|
||||
"K17dc3a62": "数据日志",
|
||||
"Ke429194e": "处理日志",
|
||||
"K84ffb1dd": "输入调用地址、消费者IP和消费者条件查找",
|
||||
"Kb147fabc": "新建",
|
||||
"K40ca4f2": "更新",
|
||||
"K3e7aa0ad": "内容",
|
||||
"K2f5fdf5e": "调用地址",
|
||||
"K1bc5e0a3": "消费者 IP",
|
||||
"K6f39ea21": "鉴权名称",
|
||||
"K8c34c02f": "脱敏前",
|
||||
"K8e3d388d": "脱敏后"
|
||||
}
|
||||
@@ -771,5 +771,19 @@
|
||||
"K67f4e9bb": "與外部平台集成時,用於獲取 API 市場文檔信息的域名",
|
||||
"K1da86266": "無效",
|
||||
"K3a34d49b": "待更新",
|
||||
"Kd2850420": "待刪除"
|
||||
"Kd2850420": "待刪除",
|
||||
"K9ada4366": "操作成功,即將刷新頁面",
|
||||
"K9b332ab1": "請求前綴",
|
||||
"K3d78d483": "HTTP 標頭",
|
||||
"K17dc3a62": "數據日誌",
|
||||
"Ke429194e": "處理日誌",
|
||||
"K84ffb1dd": "輸入調用地址、消費者 IP 和消費者條件進行查找",
|
||||
"Kb147fabc": "新建",
|
||||
"K40ca4f2": "更新",
|
||||
"K3e7aa0ad": "內容",
|
||||
"K2f5fdf5e": "調用地址",
|
||||
"K1bc5e0a3": "消費者 IP",
|
||||
"K6f39ea21": "鑑權名稱",
|
||||
"K8c34c02f": "脫敏前",
|
||||
"K8e3d388d": "脫敏後"
|
||||
}
|
||||
|
||||
@@ -1,115 +1,157 @@
|
||||
|
||||
import { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { COLUMNS_TITLE } from "@common/const/const";
|
||||
import { EntityItem } from "@common/const/type";
|
||||
|
||||
export type PartitionConfigFieldType = {
|
||||
name?: string;
|
||||
id?: string;
|
||||
description?: string;
|
||||
prefix?:string
|
||||
url?:string
|
||||
managerAddress?:string
|
||||
canDelete?:boolean
|
||||
name?: string;
|
||||
id?: string;
|
||||
description?: string;
|
||||
prefix?: string
|
||||
url?: string
|
||||
managerAddress?: string
|
||||
canDelete?: boolean
|
||||
};
|
||||
|
||||
export type PartitionCertTableListItem = {
|
||||
id:string;
|
||||
name: string;
|
||||
domains:string[];
|
||||
notAfter:string;
|
||||
notBefore:string;
|
||||
updater:EntityItem;
|
||||
updateTime:string;
|
||||
id: string;
|
||||
name: string;
|
||||
domains: string[];
|
||||
notAfter: string;
|
||||
notBefore: string;
|
||||
updater: EntityItem;
|
||||
updateTime: string;
|
||||
};
|
||||
|
||||
export type PartitionCertConfigFieldType = {
|
||||
id?:string
|
||||
key:string
|
||||
pem:string
|
||||
id?: string
|
||||
key: string
|
||||
pem: string
|
||||
};
|
||||
|
||||
export type PartitionCertConfigProps = {
|
||||
type:'add'|'edit'
|
||||
entity?:PartitionCertConfigFieldType
|
||||
type: 'add' | 'edit'
|
||||
entity?: PartitionCertConfigFieldType
|
||||
}
|
||||
|
||||
export type PartitionCertConfigHandle = {
|
||||
save:()=>Promise<boolean|string>
|
||||
save: () => Promise<boolean | string>
|
||||
}
|
||||
|
||||
export type PartitionClusterFieldType = {
|
||||
name?: string;
|
||||
id?: string;
|
||||
description?: string;
|
||||
address?:string;
|
||||
protocol?:'http'|'https'
|
||||
name?: string;
|
||||
id?: string;
|
||||
description?: string;
|
||||
address?: string;
|
||||
protocol?: 'http' | 'https'
|
||||
};
|
||||
|
||||
export type ClusterConfigProps = {
|
||||
mode:'config' | 'retry' | 'result' | 'edit',
|
||||
clusterId?:string
|
||||
initFormValue?:{[k:string]:string|number}
|
||||
mode: 'config' | 'retry' | 'result' | 'edit',
|
||||
clusterId?: string
|
||||
initFormValue?: { [k: string]: string | number }
|
||||
}
|
||||
|
||||
export type ClusterConfigHandle = {
|
||||
save:()=>Promise<boolean|string>
|
||||
check:()=>Promise<boolean>
|
||||
save: () => Promise<boolean | string>
|
||||
check: () => Promise<boolean>
|
||||
}
|
||||
|
||||
export type PartitionClusterTableListItem = {
|
||||
id:string;
|
||||
name: string;
|
||||
status:0|1;
|
||||
description:string;
|
||||
id: string;
|
||||
name: string;
|
||||
status: 0 | 1;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type PartitionClusterNodeTableListItem = {
|
||||
id:string;
|
||||
name: string;
|
||||
managerAddress:string[];
|
||||
serviceAddress:string[];
|
||||
peerAddress:string[];
|
||||
status:0|1;
|
||||
id: string;
|
||||
name: string;
|
||||
managerAddress: string[];
|
||||
serviceAddress: string[];
|
||||
peerAddress: string[];
|
||||
status: 0 | 1;
|
||||
};
|
||||
|
||||
export type PartitionClusterNodeModalTableListItem = {
|
||||
id: string,
|
||||
name: string,
|
||||
managerAddress: [],
|
||||
serviceAddress: [],
|
||||
peerAddress: string,
|
||||
status: string
|
||||
id: string,
|
||||
name: string,
|
||||
managerAddress: [],
|
||||
serviceAddress: [],
|
||||
peerAddress: string,
|
||||
status: string
|
||||
}
|
||||
|
||||
export type NodeModalFieldType = {
|
||||
address:string
|
||||
address: string
|
||||
}
|
||||
|
||||
|
||||
export type NodeModalHandle = {
|
||||
save:()=>void
|
||||
save: () => void
|
||||
}
|
||||
export type NodeModalPropsType = {
|
||||
changeStatus:(status:ClusterPageShowStatus)=>void
|
||||
getClusterInfo:()=>void
|
||||
status:ClusterPageShowStatus
|
||||
changeStatus: (status: ClusterPageShowStatus) => void
|
||||
getClusterInfo: () => void
|
||||
status: ClusterPageShowStatus
|
||||
}
|
||||
|
||||
export type ClusterPageShowStatus = 'view'|'preview'|'edit'
|
||||
export type ClusterPageShowStatus = 'view' | 'preview' | 'edit'
|
||||
export type PartitionTableListItem = {
|
||||
id:string;
|
||||
name: string;
|
||||
clusterNum:number;
|
||||
updater:EntityItem;
|
||||
updateTime:string;
|
||||
id: string;
|
||||
name: string;
|
||||
clusterNum: number;
|
||||
updater: EntityItem;
|
||||
updateTime: string;
|
||||
};
|
||||
|
||||
export type SimplePartition = EntityItem & { clusters: (EntityItem & {description:string})[] }
|
||||
export type SimplePartition = EntityItem & { clusters: (EntityItem & { description: string })[] }
|
||||
|
||||
export type PartitionDashboardConfigFieldType = {
|
||||
driver:string
|
||||
config:{
|
||||
org:string
|
||||
token:string
|
||||
addr:string
|
||||
driver: string
|
||||
config: {
|
||||
org: string
|
||||
token: string
|
||||
addr: string
|
||||
}
|
||||
}
|
||||
export type PartitionDataLogHeaderListFieldType = {
|
||||
key: string
|
||||
value: string
|
||||
|
||||
}
|
||||
export type PartitionDataLogConfigFieldType = {
|
||||
headers: PartitionDataLogHeaderListFieldType[]
|
||||
url: string
|
||||
}
|
||||
|
||||
export const PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS: PageProColumns<PartitionDataLogConfigFieldType & { _id: string }>[] = [
|
||||
{
|
||||
title: ('标签'),
|
||||
dataIndex: 'key',
|
||||
formItemProps: {
|
||||
className: 'p-0 bg-transparent border-none',
|
||||
rootClassName: 'test',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
whitespace: true
|
||||
},
|
||||
],
|
||||
},
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: ('内容'),
|
||||
dataIndex: 'value',
|
||||
formItemProps: {
|
||||
className: 'p-0 bg-transparent border-none'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: COLUMNS_TITLE.operate,
|
||||
valueType: 'option',
|
||||
btnNums: 2,
|
||||
render: () => null
|
||||
},
|
||||
];
|
||||
@@ -239,7 +239,7 @@ export const AiServiceSubscriberConfig = forwardRef<AiServiceSubscriberConfigHan
|
||||
getAiServiceList()
|
||||
}, [serviceId]);
|
||||
|
||||
return (<WithPermission access="team.service.subscription.add">
|
||||
return (<WithPermission access="">
|
||||
<Form
|
||||
layout='vertical'
|
||||
labelAlign='left'
|
||||
|
||||
@@ -106,9 +106,9 @@ const AiSettingList = ()=>{
|
||||
</a>
|
||||
<div>
|
||||
<CancelBtn/>
|
||||
<WithPermission access="system.devops.ai_provider.edit" showDisabled={false}>
|
||||
<OkBtn/>
|
||||
</WithPermission>
|
||||
{
|
||||
checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn/> : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect } from "react";
|
||||
import { PartitionDashboardConfigFieldType } from "../../const/partitions/types";
|
||||
import { DASHBOARD_SETTING_DRIVER_OPTION_LIST } from "../../const/partitions/const";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission";
|
||||
import { BasicResponse, PLACEHOLDER, STATUS_CODE, VALIDATE_MESSAGE } from "@common/const/const";
|
||||
import { BasicResponse, PLACEHOLDER, STATUS_CODE } from "@common/const/const";
|
||||
import { useFetch } from "@common/hooks/http";
|
||||
import { $t } from "@common/locales";
|
||||
|
||||
@@ -29,10 +29,10 @@ export type DashboardSettingEditProps = {
|
||||
fetchData<BasicResponse<{info: PartitionDashboardConfigFieldType}>>('monitor/config',{method: 'POST',body:JSON.stringify(value),eoParams:{}}).then(response=>{
|
||||
const {code,msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
message.success(msg || '操作成功,即将刷新页面')
|
||||
message.success(msg || $t('操作成功,即将刷新页面'))
|
||||
refreshData?.()
|
||||
}else{
|
||||
message.error(msg || '操作失败')
|
||||
message.error(msg || $t('操作失败'))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import EditableTable from "@common/components/aoplatform/EditableTable"
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission"
|
||||
import { BasicResponse, PLACEHOLDER, STATUS_CODE } from "@common/const/const"
|
||||
import { useFetch } from "@common/hooks/http"
|
||||
import { $t } from "@common/locales"
|
||||
import { PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS, PartitionDataLogConfigFieldType, PartitionDataLogHeaderListFieldType } from "@core/const/partitions/types"
|
||||
import { Button, Form, Input, message } from "antd"
|
||||
import { useEffect } from "react"
|
||||
|
||||
export type DashboardPageShowStatus = 'view' | 'edit'
|
||||
export type DashboardSettingEditProps = {
|
||||
changeStatus: (status: DashboardPageShowStatus) => void
|
||||
refreshData: () => void
|
||||
data?: PartitionDataLogConfigFieldType
|
||||
}
|
||||
const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
|
||||
const { changeStatus, refreshData, data } = props
|
||||
const [form] = Form.useForm();
|
||||
const { fetchData } = useFetch()
|
||||
|
||||
const onFinish = () => {
|
||||
form.validateFields().then((value) => {
|
||||
const formData = {
|
||||
config: {
|
||||
url: value.url,
|
||||
headers: value.headers.filter((item: PartitionDataLogHeaderListFieldType) => item.key).map((item: PartitionDataLogHeaderListFieldType) => ({key:item.key, value:item.value || ''}))
|
||||
}
|
||||
}
|
||||
fetchData<BasicResponse<{ info: PartitionDataLogConfigFieldType }>>('log/loki', { method: 'POST', body: JSON.stringify(formData), eoParams: {} }).then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t('操作成功,即将刷新页面'))
|
||||
refreshData?.()
|
||||
} else {
|
||||
message.error(msg || $t('操作失败'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => { form.setFieldsValue(data) }, [data])
|
||||
|
||||
useEffect(() => {
|
||||
return (form.setFieldsValue({}))
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div className="overflow-auto h-full">
|
||||
<WithPermission access={''} >
|
||||
<Form
|
||||
form={form}
|
||||
className="mx-auto flex flex-col justify-between h-full"
|
||||
layout="vertical"
|
||||
onFinish={onFinish}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<PartitionDataLogConfigFieldType>
|
||||
label={$t("请求前缀")}
|
||||
name="url"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<PartitionDataLogConfigFieldType>
|
||||
label={$t("HTTP 头部")}
|
||||
name="headers"
|
||||
>
|
||||
<EditableTable<PartitionDataLogConfigFieldType & { _id: string }>
|
||||
configFields={PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="flex gap-btnbase">
|
||||
<WithPermission access='system.devops.data_source.edit'>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{$t('保存')}
|
||||
</Button>
|
||||
</WithPermission>
|
||||
<Button type="default" onClick={() => changeStatus('view')}>
|
||||
{$t('取消')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</WithPermission>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataLogSettingEdit;
|
||||
@@ -57,7 +57,7 @@ const CertConfigModal = forwardRef<PartitionCertConfigHandle,PartitionCertConfig
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (<WithPermission access={type === 'edit' ? 'system.devops.ssl_certificate.edit':'system.devops.ssl_certificate.add'}>
|
||||
return (<WithPermission access=''>
|
||||
<Form
|
||||
layout='vertical'
|
||||
labelAlign='left'
|
||||
@@ -187,7 +187,7 @@ const PartitionInsideCert:FC = ()=>{
|
||||
switch (type){
|
||||
case 'add':
|
||||
title=$t('添加证书')
|
||||
content= <CertConfigModal ref={addRef} type="add"/>
|
||||
content= <WithPermission access='system.devops.ssl_certificate.add'><CertConfigModal ref={addRef} type="add"/></WithPermission>
|
||||
break;
|
||||
case 'edit':{
|
||||
title=$t('修改证书')
|
||||
@@ -195,7 +195,7 @@ const PartitionInsideCert:FC = ()=>{
|
||||
const {code,data,msg} = await fetchData<BasicResponse<{cert:{key:string, pem:string}}>>('certificate',{method:'GET',eoParams:{id:entity!.id}})
|
||||
message.destroy()
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
content= <CertConfigModal ref={editRef} type="edit" entity={{...data.cert,id:entity!.id}}/>
|
||||
content= <WithPermission access={'system.devops.ssl_certificate.edit'}><CertConfigModal ref={editRef} type="edit" entity={{...data.cert,id:entity!.id}}/></WithPermission>
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return
|
||||
|
||||
@@ -1,93 +1,175 @@
|
||||
import { FC, useEffect, useState} from "react";
|
||||
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
|
||||
import {App, Button, Card, Col, Row, Spin, Tag} from "antd";
|
||||
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
|
||||
import {useFetch} from "@common/hooks/http.ts";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext.tsx";
|
||||
import { App, Button, Card, Col, Row, Spin, Tag } from "antd";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
|
||||
import { useFetch } from "@common/hooks/http.ts";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
|
||||
import { $t } from "@common/locales/index.ts";
|
||||
import DashboardSettingEdit, { DashboardPageShowStatus } from "./DashboardSettingEdit.tsx";
|
||||
import { PartitionDashboardConfigFieldType } from "@core/const/partitions/types.ts";
|
||||
import { PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS, PartitionDashboardConfigFieldType, PartitionDataLogConfigFieldType } from "@core/const/partitions/types.ts";
|
||||
import PageList from "@common/components/aoplatform/PageList.tsx";
|
||||
import DataLogSettingEdit from "./DataLogSettingEdit.tsx";
|
||||
|
||||
const PartitionInsideDashboardSetting:FC = ()=> {
|
||||
const {setBreadcrumb} = useBreadcrumb()
|
||||
const {message} = App.useApp()
|
||||
const {fetchData} = useFetch()
|
||||
const [data, setData] = useState<PartitionDashboardConfigFieldType>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [showStatus, setShowStatus] = useState<DashboardPageShowStatus>('view')
|
||||
const PartitionInsideDashboardSetting: FC = () => {
|
||||
const { setBreadcrumb } = useBreadcrumb()
|
||||
const { message } = App.useApp()
|
||||
const { fetchData } = useFetch()
|
||||
const [data, setData] = useState<PartitionDashboardConfigFieldType>()
|
||||
const [dataLogData, setDataLogData] = useState<PartitionDataLogConfigFieldType>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [dataLogLoading, setDataLogLoading] = useState<boolean>(false)
|
||||
const [showGraphStatus, setShowGraphStatus] = useState<DashboardPageShowStatus>('view')
|
||||
const [showDataLogStatus, setShowDataLogStatus] = useState<DashboardPageShowStatus>('view')
|
||||
|
||||
const getDashboardSettingInfo = () => {
|
||||
setLoading(true)
|
||||
return fetchData<BasicResponse<{ nodes:PartitionDashboardConfigFieldType[] }>>('monitor/config', {method: 'GET',eoTransformKeys:[]}).then(response => {
|
||||
const {code, data, msg} = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
data?.info?.driver && setData(data.info)
|
||||
setShowStatus('view')
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch(() => {
|
||||
return {data: [], success: false}
|
||||
}).finally(()=>{
|
||||
setLoading(false)
|
||||
const getDashboardSettingInfo = () => {
|
||||
setLoading(true)
|
||||
return fetchData<BasicResponse<{ nodes: PartitionDashboardConfigFieldType[] }>>('monitor/config', { method: 'GET', eoTransformKeys: [] }).then(response => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
data?.info?.driver && setData(data.info)
|
||||
setShowGraphStatus('view')
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch(() => {
|
||||
return { data: [], success: false }
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
const getDataLogSettingInfo = () => {
|
||||
setDataLogLoading(true)
|
||||
return fetchData<BasicResponse<{ nodes: PartitionDataLogConfigFieldType[] }>>('log/loki', { method: 'GET', eoTransformKeys: [] }).then(response => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
data?.info && setDataLogData({
|
||||
url: data.info?.config?.url || '',
|
||||
headers: data.info?.config?.headers || []
|
||||
})
|
||||
}
|
||||
setShowDataLogStatus('view')
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch(() => {
|
||||
return { data: [], success: false }
|
||||
}).finally(() => {
|
||||
setDataLogLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumb([
|
||||
{ title: $t('数据源') }
|
||||
])
|
||||
getDashboardSettingInfo()
|
||||
getDataLogSettingInfo()
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumb([
|
||||
{title: $t('数据源')}
|
||||
])
|
||||
getDashboardSettingInfo()
|
||||
}, []);
|
||||
|
||||
const setDashboardSettingBtn = ()=>{
|
||||
return (<>
|
||||
{showStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
|
||||
<Button type="primary" onClick={() => setShowStatus('edit')}>{$t('修改配置')}</Button>
|
||||
</WithPermission> }</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<InsidePage
|
||||
pageTitle={$t('数据源')}
|
||||
description={$t("设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。")}
|
||||
showBorder={false}
|
||||
scrollPage={true}
|
||||
>
|
||||
<div className="flex flex-col h-full overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
|
||||
<Spin wrapperClassName=" h-full flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
|
||||
<div className="h-full overflow-auto">
|
||||
<Card
|
||||
classNames={{
|
||||
body: `overflow-auto ${(!data || !data?.driver) && showStatus === 'view' ? 'hidden': ''}`,
|
||||
}}
|
||||
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
|
||||
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('统计图表')}</span>
|
||||
{!loading && !data?.driver && <Tag color='#f50'>{ $t('未配置')}
|
||||
</Tag>}</div>}
|
||||
|
||||
extra={setDashboardSettingBtn()}>
|
||||
{showStatus === 'view'&& data && data.driver && DashboardConfigPreview(data) }
|
||||
{showStatus !== 'view' && <DashboardSettingEdit data={data} changeStatus={setShowStatus} refreshData={getDashboardSettingInfo} />}
|
||||
</Card>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
</InsidePage>
|
||||
</>
|
||||
const setDashboardSettingBtn = () => {
|
||||
return (<>
|
||||
{showGraphStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
|
||||
<Button type="primary" onClick={() => setShowGraphStatus('edit')}>{$t('修改配置')}</Button>
|
||||
</WithPermission>}</>
|
||||
)
|
||||
}
|
||||
const setDataLogSettingBtn = () => {
|
||||
return (<>
|
||||
{showDataLogStatus === 'view' && <WithPermission access="system.devops.data_source.edit" key="changeClusterConfig">
|
||||
<Button type="primary" onClick={() => setShowDataLogStatus('edit')}>{$t('修改配置')}</Button>
|
||||
</WithPermission>}</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<InsidePage
|
||||
pageTitle={$t('数据源')}
|
||||
description={$t("设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。")}
|
||||
showBorder={false}
|
||||
scrollPage={false}
|
||||
>
|
||||
<div className="flex flex-col overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
|
||||
<Spin wrapperClassName="flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading}>
|
||||
<div className="h-full overflow-auto">
|
||||
<Card
|
||||
classNames={{
|
||||
body: `overflow-auto ${(!data || !data?.driver) && showGraphStatus === 'view' ? 'hidden' : ''}`,
|
||||
}}
|
||||
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
|
||||
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('统计图表')}</span>
|
||||
{!loading && !data?.driver && <Tag color='#f50'>{$t('未配置')}
|
||||
</Tag>}</div>}
|
||||
|
||||
extra={setDashboardSettingBtn()}>
|
||||
{showGraphStatus === 'view' && data && data.driver && DashboardConfigPreview(data)}
|
||||
{showGraphStatus !== 'view' && <DashboardSettingEdit data={data} changeStatus={setShowGraphStatus} refreshData={getDashboardSettingInfo} />}
|
||||
</Card>
|
||||
</div>
|
||||
</Spin>
|
||||
<Spin wrapperClassName="flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={dataLogLoading}>
|
||||
<div className="h-full overflow-auto">
|
||||
<Card
|
||||
classNames={{
|
||||
body: `overflow-auto ${(!dataLogData) && showDataLogStatus === 'view' ? 'hidden' : ''}`,
|
||||
}}
|
||||
className="overflow-hidden mt-[30px] w-full max-h-full flex flex-col justify-between"
|
||||
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('数据日志')}</span>
|
||||
{!dataLogLoading && !dataLogData && <Tag color='#f50'>{$t('未配置')}
|
||||
</Tag>}</div>}
|
||||
|
||||
extra={setDataLogSettingBtn()}>
|
||||
{showDataLogStatus === 'view' && dataLogData && DataLogConfigPreview(dataLogData)}
|
||||
{showDataLogStatus !== 'view' && <DataLogSettingEdit data={dataLogData} changeStatus={setShowDataLogStatus} refreshData={getDataLogSettingInfo} />}
|
||||
</Card>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
</InsidePage>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function DashboardConfigPreview (x:PartitionDashboardConfigFieldType){
|
||||
return <div className="flex flex-col gap-[4px] ">
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('数据源')}:</Col><Col>{x?.driver}</Col></Row>
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('地址(IP:端口)')}:</Col><Col>{x?.config?.addr}</Col></Row>
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('组织(Organization)')}:</Col><Col>{x?.config?.org}</Col></Row>
|
||||
</div>}
|
||||
export function DashboardConfigPreview(x: PartitionDashboardConfigFieldType) {
|
||||
return <div className="flex flex-col gap-[4px] ">
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('数据源')}:</Col><Col>{x?.driver}</Col></Row>
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('地址(IP:端口)')}:</Col><Col>{x?.config?.addr}</Col></Row>
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('组织(Organization)')}:</Col><Col>{x?.config?.org}</Col></Row>
|
||||
</div>
|
||||
}
|
||||
export function DataLogConfigPreview(x: PartitionDataLogConfigFieldType) {
|
||||
const columns = PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS.map(x => {
|
||||
return {
|
||||
...x,
|
||||
title: (<span title={$t(x.title as string)}>{$t(x.title as string)}</span>)
|
||||
}
|
||||
})
|
||||
const getTableList = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
data: x?.headers || [],
|
||||
success: true
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="flex flex-col gap-[4px] ">
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('请求前缀')}:</Col><Col>{x?.url}</Col></Row>
|
||||
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('HTTP 头部')}:</Col><Col className="w-full">
|
||||
<div className="w-full h-full p-[20px]">
|
||||
<PageList
|
||||
id="global_role"
|
||||
tableClass="role_table mb-btnrbase"
|
||||
primaryKey="'key'"
|
||||
columns={[...columns]}
|
||||
request={() => getTableList()}
|
||||
showPagination={false}
|
||||
noScroll={true}
|
||||
/>
|
||||
</div>
|
||||
</Col></Row>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default PartitionInsideDashboardSetting
|
||||
@@ -79,7 +79,7 @@ const DataMasking = (props: any) => {
|
||||
{
|
||||
title: '',
|
||||
key: 'option',
|
||||
btnNums: rowOperation.length -1,
|
||||
btnNums: rowOperation.length,
|
||||
fixed: 'right',
|
||||
valueType: 'option',
|
||||
render: (_: React.ReactNode, entity: any) => [
|
||||
@@ -233,7 +233,6 @@ const DataMasking = (props: any) => {
|
||||
* @param entity
|
||||
*/
|
||||
const openLogsModal = (entity: any) => {
|
||||
console.log('日志', entity);
|
||||
setStrategy(entity.id)
|
||||
setModalVisible(true)
|
||||
}
|
||||
@@ -379,10 +378,11 @@ const DataMasking = (props: any) => {
|
||||
<Modal
|
||||
title={$t('处理日志')}
|
||||
visible={modalVisible}
|
||||
destroyOnClose={true}
|
||||
onCancel={handleCloseModal}
|
||||
footer={null}
|
||||
wrapClassName="modal-without-footer"
|
||||
width={1000}
|
||||
width={1100}
|
||||
maskClosable={true}
|
||||
>
|
||||
<div className="pb-btnybase flex flex-nowrap flex-col h-full w-full items-center justify-between">
|
||||
|
||||
@@ -27,7 +27,7 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
|
||||
filters: true,
|
||||
onFilter: false ,
|
||||
ellipsis: true,
|
||||
width: 140,
|
||||
width: 110,
|
||||
valueEnum: new Map(
|
||||
Object.keys(StrategyStatusEnum).map(key=>
|
||||
[key,
|
||||
@@ -40,6 +40,7 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
|
||||
filters: true,
|
||||
onFilter: false ,
|
||||
ellipsis: true,
|
||||
width: 90,
|
||||
valueEnum: {
|
||||
false: { text: <span className="text-status_success">{$t('启用')}</span> },
|
||||
true: { text: <span className="text-status_fail">{$t('禁用')}</span> }
|
||||
@@ -53,7 +54,8 @@ export const DATA_MASKING_TABLE_COLUMNS: PageProColumns<any>[] = [
|
||||
{
|
||||
title: ('处理数'),
|
||||
dataIndex: 'processedTotal',
|
||||
ellipsis: true
|
||||
ellipsis: true,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: ('更新者'),
|
||||
@@ -83,10 +85,10 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: ('消费者IP'),
|
||||
title: ('消费者 IP'),
|
||||
dataIndex: 'remote_ip',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: ('消费者'),
|
||||
@@ -96,14 +98,14 @@ export const DATA_MASKING_TABLE_LOG_COLUMNS: PageProColumns<any>[] = [
|
||||
},
|
||||
{
|
||||
title: ('鉴权名称'),
|
||||
dataIndex: 'authorization',
|
||||
dataIndex: ['authorization', 'name'],
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: ('时间'),
|
||||
dataIndex: 'record_time',
|
||||
width: 150,
|
||||
width: 110,
|
||||
ellipsis: true
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Codebox } from "@common/components/postcat/api/Codebox";
|
||||
import { Codebox, codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
|
||||
import { RouterParams } from "@common/const/type";
|
||||
import { $t } from "@common/locales";
|
||||
import { App, Button, message, Switch, Modal, Spin } from 'antd'
|
||||
import { message, Spin } from 'antd'
|
||||
import { useFetch } from "@common/hooks/http";
|
||||
import { useEffect, useState } from "react";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { contentTypeToLanguageMap } from "@common/const/policy/consts";
|
||||
type LogItems = {
|
||||
id: string;
|
||||
origin: string;
|
||||
@@ -18,7 +17,15 @@ const DataMaskingCompare = () => {
|
||||
const { fetchData } = useFetch()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [originValue, setOriginValue] = useState('')
|
||||
const [targetValue, settTargetValue] = useState('')
|
||||
const [targetValue, setTargetValue] = useState('')
|
||||
const [language, setLanguage] = useState<codeBoxLanguagesType>('json')
|
||||
const getMonacoEditorLanguage = (contentType: string): codeBoxLanguagesType => {
|
||||
// 提取主类型,忽略参数(如 "; charset=utf-8")
|
||||
const mainType = contentType.split(";")[0].trim().toLowerCase();
|
||||
|
||||
// 根据映射表获取语言,默认返回 "plaintext"
|
||||
return contentTypeToLanguageMap[mainType] || "json";
|
||||
};
|
||||
const getLogData = () => {
|
||||
setLoading(true)
|
||||
return fetchData<BasicResponse<{ log: LogItems }>>(`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/log`,
|
||||
@@ -33,8 +40,10 @@ const DataMaskingCompare = () => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
const { log } = data
|
||||
setOriginValue(log.origin || '')
|
||||
settTargetValue(log.target || '')
|
||||
const docLanguage = getMonacoEditorLanguage(log.content_type)
|
||||
setLanguage(docLanguage)
|
||||
setOriginValue(docLanguage === 'json' ? JSON.stringify(JSON.parse(log.origin || ''), null, 2) : log.origin || '')
|
||||
setTargetValue(docLanguage === 'json' ? JSON.stringify(JSON.parse(log.target || ''), null, 2) : log.target || '')
|
||||
setLoading(false)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
@@ -42,17 +51,6 @@ const DataMaskingCompare = () => {
|
||||
}).catch(() => {
|
||||
return { data: [], success: false }
|
||||
}).finally(() => {
|
||||
const aa = `{
|
||||
"code": {
|
||||
"gg": "gg",
|
||||
"gg1": "gg",
|
||||
"gg2": "gg",
|
||||
"gg3": "gg",
|
||||
"gg4": "gg"
|
||||
}
|
||||
}`
|
||||
setOriginValue(JSON.stringify(JSON.parse(aa), null, 2))
|
||||
settTargetValue(JSON.stringify(JSON.parse(aa), null, 2))
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
@@ -64,24 +62,26 @@ const DataMaskingCompare = () => {
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<div className="w-1/2 p-2 h-full">
|
||||
<div className="h-[30px] bg-gray-200 mb-2 flex items-center justify-center">
|
||||
脱敏前
|
||||
{$t('脱敏前')}
|
||||
</div>
|
||||
<div style={{ height: 'calc(100vh - 50px)' }}>
|
||||
<Codebox
|
||||
language='json'
|
||||
language={language}
|
||||
height="100%"
|
||||
width="100%"
|
||||
value={originValue}
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/2 p-2 h-full">
|
||||
<div className="h-[30px] bg-green-100 mb-2 flex items-center justify-center">
|
||||
脱敏后
|
||||
{$t('脱敏后')}
|
||||
</div>
|
||||
<div style={{ height: 'calc(100vh - 50px)' }}>
|
||||
<Codebox
|
||||
language='json'
|
||||
language={language}
|
||||
width="100%"
|
||||
height="100%"
|
||||
value={targetValue}
|
||||
|
||||
@@ -1,46 +1,62 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import { DataMaskLogItem } from "@common/const/policy/type";
|
||||
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { $t } from "@common/locales";
|
||||
import { App, Button, message, DatePicker, Modal } from 'antd'
|
||||
import { Button, message } from 'antd'
|
||||
|
||||
import { DATA_MASKING_TABLE_LOG_COLUMNS, DATA_MASKING_TABLE_COLUMNS } from './DataMaskingColumn';
|
||||
import { DATA_MASKING_TABLE_LOG_COLUMNS } from './DataMaskingColumn';
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
|
||||
import { ActionType } from '@ant-design/pro-components';
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { RouterParams } from '@common/const/type';
|
||||
import { useFetch } from '@common/hooks/http';
|
||||
import WithPermission from '@common/components/aoplatform/WithPermission';
|
||||
import TimeRangeSelector, { TimeRange } from '@common/components/aoplatform/TimeRangeSelector';
|
||||
import { SearchBody } from '@dashboard/const/type';
|
||||
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission';
|
||||
const { RangePicker } = DatePicker;
|
||||
const DataMaskingLogModal = (props: any) => {
|
||||
const { strategy } = props;
|
||||
const { state, accessData } = useGlobalContext()
|
||||
const { serviceId, teamId } = useParams<RouterParams>()
|
||||
const [datePickerValue, setDatePickerValue] = useState<any>();
|
||||
const [queryData, setQueryData] = useState<SearchBody>({})
|
||||
|
||||
const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
|
||||
const [queryData, setQueryData] = useState<SearchBody>({
|
||||
start: currentSecond - 24 * 60 * 60,
|
||||
end: currentSecond
|
||||
})
|
||||
/**
|
||||
* 请求数据
|
||||
*/
|
||||
const { fetchData } = useFetch()
|
||||
/**
|
||||
* 列表ref
|
||||
*/
|
||||
* 列表ref
|
||||
*/
|
||||
const pageListRef = useRef<ActionType>(null);
|
||||
/**
|
||||
* 搜索关键字
|
||||
*/
|
||||
const [searchWord, setSearchWord] = useState<string>('')
|
||||
/**
|
||||
* 重置时间范围
|
||||
*/
|
||||
let resetTimeRange = () => {}
|
||||
/**
|
||||
* 时间按钮
|
||||
*/
|
||||
const [timeButton, setTimeButton] = useState<'' | 'hour' | 'day' | 'threeDays' | 'sevenDays'>('day');
|
||||
/**
|
||||
* 绑定时间范围组件
|
||||
* @param instance
|
||||
*/
|
||||
const bindRef = (instance: any) => {
|
||||
resetTimeRange = instance.reset
|
||||
};
|
||||
/**
|
||||
* 操作列
|
||||
*/
|
||||
const operation: PageProColumns<any>[] = [
|
||||
{
|
||||
title: '操作',
|
||||
title: '',
|
||||
key: 'option',
|
||||
btnNums: 1,
|
||||
fixed: 'right',
|
||||
@@ -54,7 +70,7 @@ const DataMaskingLogModal = (props: any) => {
|
||||
url += `/${teamId}`
|
||||
}
|
||||
return [
|
||||
<TableBtnWithPermission access={`${serviceId === undefined ? 'system.devops' : 'team.service'}.policy.view`} key="view" btnType="view" onClick={() => { window.open(url,'_blank') }} btnTitle="查看" />
|
||||
<TableBtnWithPermission access={`${serviceId === undefined ? 'system.devops' : 'team.service'}.policy.view`} key="view" btnType="view" onClick={() => { window.open(url, '_blank') }} btnTitle="查看" />
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -68,11 +84,11 @@ const DataMaskingLogModal = (props: any) => {
|
||||
const columns = useMemo(() => {
|
||||
const res = DATA_MASKING_TABLE_LOG_COLUMNS.map(x => {
|
||||
if (x.dataIndex === 'url') {
|
||||
x.render = (text: any, record: any) => <><span className='text-green-500'>{record.method}</span> <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 {
|
||||
...x,
|
||||
title: typeof x.title === 'string' ? $t(x.title as string) : x.title
|
||||
title: (<span title={$t(x.title as string)}>{$t(x.title as string)}</span>)
|
||||
}
|
||||
})
|
||||
return res
|
||||
@@ -88,7 +104,7 @@ const DataMaskingLogModal = (props: any) => {
|
||||
current: number;
|
||||
}) => {
|
||||
return fetchData<BasicResponse<{ logs: DataMaskLogItem[], total: number }>>(
|
||||
`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/list`,
|
||||
`strategy/${serviceId === undefined ? 'global' : 'service'}/data-masking/logs`,
|
||||
{
|
||||
method: 'GET',
|
||||
eoParams: {
|
||||
@@ -106,204 +122,9 @@ const DataMaskingLogModal = (props: any) => {
|
||||
).then(response => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
const mockData: any = [
|
||||
{
|
||||
id: '12334',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff1',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff2',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff3',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff4',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff5',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff6',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff7',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff8',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff9',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff11',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:12',
|
||||
},
|
||||
{
|
||||
id: 'fff22',
|
||||
service: {
|
||||
id: 'xxx',
|
||||
name: 'xxx'
|
||||
},
|
||||
url: 'url',
|
||||
remote_ip: '9234923',
|
||||
consumer: {
|
||||
id: 'yyy',
|
||||
name: 'yyy'
|
||||
},
|
||||
method: 'GET',
|
||||
authorization: 'authorization',
|
||||
record_time: '2021-09-09 12:12:11',
|
||||
}
|
||||
|
||||
]
|
||||
// 保存数据
|
||||
return {
|
||||
data: mockData,
|
||||
data: data.logs || [],
|
||||
total: data.total,
|
||||
success: true
|
||||
}
|
||||
@@ -320,24 +141,13 @@ const DataMaskingLogModal = (props: any) => {
|
||||
const handleTimeRangeChange = (timeRange: TimeRange) => {
|
||||
setQueryData(pre => ({ ...pre, ...timeRange } as SearchBody))
|
||||
manualReloadTable()
|
||||
|
||||
|
||||
};
|
||||
const handleDatePickerChange = (dates: any) => {
|
||||
if (dates && Array.isArray(dates) && dates.length === 2) {
|
||||
const [startDate, endDate] = dates;
|
||||
const start = startDate!.startOf('day').unix(); // 开始日期的00:00:00
|
||||
const end = endDate!.endOf('day').unix(); // 结束日期的23:59:59
|
||||
handleTimeRangeChange({ start, end });
|
||||
} else {
|
||||
handleTimeRangeChange({ start: null, end: null})
|
||||
}
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
setDatePickerValue(null)
|
||||
handleTimeRangeChange({ start: null, end: null})
|
||||
|
||||
resetTimeRange()
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full h-full p-[20px]">
|
||||
@@ -348,11 +158,17 @@ const DataMaskingLogModal = (props: any) => {
|
||||
columns={[...columns, ...operation]}
|
||||
afterNewBtn={
|
||||
[<div className="flex items-center flex-wrap p-[10px] px-btnbase content-before bg-MAIN_BG ">
|
||||
<RangePicker
|
||||
onChange={handleDatePickerChange}
|
||||
value={datePickerValue} />
|
||||
<div className="flex [&>.reset-btn]:!h-auto flex-nowrap items-center ml-[10px]">
|
||||
<Button className="reset-btn" onClick={resetQuery}>{$t('重置')}</Button>
|
||||
<TimeRangeSelector
|
||||
labelSize="small"
|
||||
bindRef={bindRef}
|
||||
hideBtns={['hour']}
|
||||
defaultTimeButton="day"
|
||||
initialTimeButton={timeButton}
|
||||
onTimeButtonChange={setTimeButton}
|
||||
initialDatePickerValue={datePickerValue}
|
||||
onTimeRangeChange={handleTimeRangeChange} />
|
||||
<div className="flex flex-nowrap items-center pt-btnybase">
|
||||
<Button onClick={resetQuery}>{$t('重置')}</Button>
|
||||
</div>
|
||||
</div>]
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
|
||||
getSystemList()
|
||||
}, [serviceId]);
|
||||
|
||||
return (<WithPermission access="team.service.subscription.add">
|
||||
return (<WithPermission access="">
|
||||
<Form
|
||||
layout='vertical'
|
||||
labelAlign='left'
|
||||
|
||||
@@ -77,7 +77,8 @@ export default defineConfig({
|
||||
target: 'http://172.18.166.219:8288/',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
open: true
|
||||
},
|
||||
logLevel:'info'
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
|
||||
import { SimpleTeamItem } from "@common/const/type";
|
||||
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
|
||||
import { GlobalProvider, useGlobalContext } from "@common/contexts/GlobalStateContext";
|
||||
import { $t } from "@common/locales";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission";
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage";
|
||||
@@ -149,7 +149,7 @@ export default function ServiceHubManagement() {
|
||||
switch (type){
|
||||
case 'add':
|
||||
title=$t('添加消费者')
|
||||
content=<ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} />
|
||||
content=<GlobalProvider><ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} /></GlobalProvider>
|
||||
break;
|
||||
// case 'edit':{
|
||||
// title='配置 Open Api'
|
||||
|
||||
@@ -20,7 +20,6 @@ func NewDynamicClient(client admin_client.Client, resource string) (*DynamicClie
|
||||
cfg, has := gateway.GetDynamicResourceDriver(resource)
|
||||
if !has {
|
||||
return nil, errors.New("resource not found")
|
||||
|
||||
}
|
||||
|
||||
return &DynamicClient{client: client, profession: cfg.Profession, driver: cfg.Driver}, nil
|
||||
|
||||
+4
-36
@@ -57,42 +57,10 @@ var dynamicResourceMap = map[string]Worker{
|
||||
Profession: ProfessionCertificate,
|
||||
Driver: "server",
|
||||
},
|
||||
//"openai": {
|
||||
// Profession: ProfessionAIProvider,
|
||||
// Driver: "openai",
|
||||
//},
|
||||
//"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",
|
||||
//},
|
||||
"loki": {
|
||||
Profession: ProfessionOutput,
|
||||
Driver: "loki",
|
||||
},
|
||||
}
|
||||
|
||||
type Worker struct {
|
||||
|
||||
@@ -7,7 +7,7 @@ var (
|
||||
)
|
||||
|
||||
type IFactory interface {
|
||||
Create(config string) (ILogDriver, error)
|
||||
Create(config string) (ILogDriver, map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type factoryManager struct {
|
||||
|
||||
+21
-7
@@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/eosc/log"
|
||||
|
||||
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||
)
|
||||
|
||||
@@ -21,7 +23,7 @@ func init() {
|
||||
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)
|
||||
}
|
||||
@@ -35,24 +37,27 @@ type Driver struct {
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
func NewDriver(config string) (*Driver, error) {
|
||||
func NewDriver(config string) (*Driver, map[string]interface{}, error) {
|
||||
cfg := new(DriverConfig)
|
||||
err := json.Unmarshal([]byte(config), cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
err = cfg.Check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
headers := map[string]string{}
|
||||
for _, h := range cfg.Header {
|
||||
headers[h.Key] = h.Value
|
||||
}
|
||||
return &Driver{
|
||||
url: cfg.URL,
|
||||
headers: headers,
|
||||
}, nil
|
||||
url: cfg.URL,
|
||||
headers: headers,
|
||||
}, map[string]interface{}{
|
||||
"url": cfg.URL,
|
||||
"headers": headers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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("end", strconv.FormatInt(now.UnixNano(), 10))
|
||||
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, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,10 +107,13 @@ func (d *Driver) LogCount(clusterId string, conditions map[string]string, spendH
|
||||
}
|
||||
queries := url.Values{}
|
||||
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, "")
|
||||
if err != nil {
|
||||
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)
|
||||
for _, l := range list {
|
||||
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("direction", "backward")
|
||||
queries.Set("start", strconv.FormatInt(start.UnixNano(), 10))
|
||||
log.Debug("query is ", queries.Get("query"))
|
||||
logs, err := d.recuseLogs(queries, end, offset)
|
||||
if err != nil {
|
||||
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 {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
log.DebugF("do request: %s", uri)
|
||||
doRequestTime := time.Now()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
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()
|
||||
respData, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ func newAIUpstream(provider string, uri model_runtime.IProviderURI) *gateway.Dyn
|
||||
"driver": "http",
|
||||
"balance": "round-robin",
|
||||
"nodes": []string{fmt.Sprintf("%s weight=100", uri.Host())},
|
||||
"pass_node": "node",
|
||||
"pass_host": "node",
|
||||
"scheme": uri.Scheme(),
|
||||
"timeout": 300000,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ package api_doc
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto"
|
||||
api_doc "github.com/APIParkLab/APIPark/service/api-doc"
|
||||
"github.com/APIParkLab/APIPark/service/service"
|
||||
@@ -20,16 +21,19 @@ type imlAPIDocModule struct {
|
||||
}
|
||||
|
||||
func (i *imlAPIDocModule) UpdateDoc(ctx context.Context, serviceId string, input *api_doc_dto.UpdateDoc) (*api_doc_dto.ApiDocDetail, error) {
|
||||
_, err := i.serviceService.Get(ctx, serviceId)
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.Id == "" {
|
||||
input.Id = uuid.New().String()
|
||||
}
|
||||
// 每个API加上前缀
|
||||
|
||||
err = i.apiDocService.UpdateDoc(ctx, serviceId, &api_doc.UpdateDoc{
|
||||
ID: input.Id,
|
||||
Content: input.Content,
|
||||
Prefix: info.Prefix,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,9 +3,9 @@ package aksk
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
|
||||
auth_driver "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver"
|
||||
|
||||
|
||||
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
|
||||
)
|
||||
|
||||
@@ -26,8 +26,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
func (a *Config) ID() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return a.Ak
|
||||
}
|
||||
|
||||
func (a *Config) Valid() ([]byte, error) {
|
||||
|
||||
@@ -99,6 +99,9 @@ func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []s
|
||||
Expire: a.ExpireTime,
|
||||
Config: authCfg,
|
||||
HideCredential: a.HideCredential,
|
||||
Label: map[string]string{
|
||||
"authorization": a.UUID,
|
||||
},
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
+167
-5
@@ -4,6 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||
|
||||
"github.com/APIParkLab/APIPark/gateway"
|
||||
|
||||
"github.com/eolinker/go-common/store"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -13,15 +20,64 @@ import (
|
||||
|
||||
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
||||
"github.com/APIParkLab/APIPark/service/log"
|
||||
log_print "github.com/eolinker/eosc/log"
|
||||
)
|
||||
|
||||
var _ ILogModule = (*imlLogModule)(nil)
|
||||
|
||||
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 {
|
||||
factory, has := log_driver.GetFactory(driver)
|
||||
if !has {
|
||||
return errors.New("driver not found")
|
||||
}
|
||||
input.Cluster = cluster.DefaultClusterID
|
||||
var cfg *string
|
||||
if input.Config != nil {
|
||||
@@ -29,10 +85,57 @@ func (i *imlLogModule) Save(ctx context.Context, driver string, input *log_dto.S
|
||||
tmp := string(data)
|
||||
cfg = &tmp
|
||||
}
|
||||
return i.service.UpdateLogSource(ctx, driver, &log.Save{
|
||||
ID: input.ID,
|
||||
Cluster: &input.Cluster,
|
||||
Config: cfg,
|
||||
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
|
||||
err := i.service.UpdateLogSource(txCtx, driver, &log.Save{
|
||||
ID: input.ID,
|
||||
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),
|
||||
}, 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/APIParkLab/APIPark/gateway"
|
||||
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,11 @@ type ILogModule interface {
|
||||
}
|
||||
|
||||
func init() {
|
||||
logModule := new(imlLogModule)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/APIParkLab/APIPark/gateway"
|
||||
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
|
||||
_ "github.com/APIParkLab/APIPark/module/strategy/driver/data-masking"
|
||||
@@ -32,6 +34,7 @@ type IStrategyModule interface {
|
||||
func init() {
|
||||
strategyModule := new(imlStrategyModule)
|
||||
autowire.Auto[IStrategyModule](func() reflect.Value {
|
||||
gateway.RegisterInitHandleFunc(strategyModule.initGateway)
|
||||
return reflect.ValueOf(strategyModule)
|
||||
})
|
||||
}
|
||||
|
||||
+12
-1
@@ -76,6 +76,17 @@ func (i *imlAPIDocService) UpdateDoc(ctx context.Context, serviceId string, inpu
|
||||
if err := doc.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if input.Prefix != "" {
|
||||
err = doc.AddPrefixInAll(input.Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
data, err := doc.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
input.Content = string(data)
|
||||
|
||||
info, err := i.store.First(ctx, map[string]interface{}{
|
||||
"service": serviceId,
|
||||
@@ -94,9 +105,9 @@ func (i *imlAPIDocService) UpdateDoc(ctx context.Context, serviceId string, inpu
|
||||
APICount: doc.APICount(),
|
||||
})
|
||||
}
|
||||
info.Content = input.Content
|
||||
info.Updater = operator
|
||||
info.UpdateAt = time.Now()
|
||||
info.Content = input.Content
|
||||
info.APICount = doc.APICount()
|
||||
return i.store.Save(ctx, info)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import "time"
|
||||
type UpdateDoc struct {
|
||||
ID string
|
||||
Content string
|
||||
Prefix string
|
||||
}
|
||||
|
||||
type Doc struct {
|
||||
|
||||
+32
-10
@@ -3,10 +3,14 @@ package api_doc
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/universally/commit"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type IAPIDocService interface {
|
||||
@@ -65,15 +69,6 @@ func (d *DocLoader) Valid() error {
|
||||
if d.openAPI3Doc.Paths == nil {
|
||||
return fmt.Errorf("openAPI3Doc.Paths is nil")
|
||||
}
|
||||
//err := d.openAPI3Doc.Validate(openapi3Loader.Context)
|
||||
//if err != nil {
|
||||
// openAPI2Doc, err := openapi2conv.FromV3(d.openAPI3Doc)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// validate.
|
||||
//
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -110,3 +105,30 @@ func (d *DocLoader) APICount() int64 {
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (d *DocLoader) AddPrefixInAll(prefix string) error {
|
||||
prefix = strings.TrimSpace(prefix)
|
||||
if prefix == "" || d.openAPI3Doc == nil || d.openAPI3Doc.Paths == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix = fmt.Sprintf("/%s/", strings.Trim(prefix, "/"))
|
||||
for path, item := range d.openAPI3Doc.Paths.Map() {
|
||||
path = strings.TrimSpace(path)
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
continue
|
||||
}
|
||||
newPath := fmt.Sprintf("%s%s", prefix, strings.TrimPrefix(path, "/"))
|
||||
d.openAPI3Doc.Paths.Delete(path)
|
||||
d.openAPI3Doc.Paths.Set(newPath, item)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DocLoader) Marshal() ([]byte, error) {
|
||||
result, err := d.openAPI3Doc.MarshalYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yaml.Marshal(result)
|
||||
}
|
||||
|
||||
@@ -162,6 +162,9 @@ func (i *imlAPIService) Save(ctx context.Context, id string, model *Edit) error
|
||||
if model.Disable != nil {
|
||||
ev.Disable = *model.Disable
|
||||
}
|
||||
if model.Upstream != nil {
|
||||
ev.Upstream = *model.Upstream
|
||||
}
|
||||
|
||||
e := i.apiInfoStore.Save(ctx, ev)
|
||||
if e != nil {
|
||||
|
||||
+16
-15
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
log_print "github.com/eolinker/eosc/log"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
log_driver "github.com/APIParkLab/APIPark/log-driver"
|
||||
@@ -26,29 +28,31 @@ type imlLogService struct {
|
||||
|
||||
func (i *imlLogService) OnComplete() {
|
||||
drivers := log_driver.Drivers()
|
||||
for _, d := range drivers {
|
||||
factory, has := log_driver.GetFactory(d)
|
||||
if len(drivers) < 1 {
|
||||
return
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, driver := range drivers {
|
||||
factory, has := log_driver.GetFactory(driver)
|
||||
if !has {
|
||||
log_print.Errorf("driver %s not found", driver)
|
||||
continue
|
||||
}
|
||||
s, err := i.GetLogSource(context.Background(), d)
|
||||
info, err := i.GetLogSource(ctx, driver)
|
||||
if err != nil {
|
||||
log_print.Errorf("get log source %s error: %s", driver, err)
|
||||
continue
|
||||
}
|
||||
driver, err := factory.Create(s.Config)
|
||||
d, _, 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(d, driver)
|
||||
|
||||
log_driver.SetDriver(driver, d)
|
||||
}
|
||||
}
|
||||
|
||||
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})
|
||||
if err != nil {
|
||||
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.UpdateAt = time.Now()
|
||||
}
|
||||
newDriver, err := factory.Create(s.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = i.store.Save(ctx, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log_driver.SetDriver(driver, newDriver)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user