This is an automated email from the ASF dual-hosted git repository.
dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new e3543ccea8 [INLONG-9938][Dashboard] Support Agent module and package
page (#9939)
e3543ccea8 is described below
commit e3543ccea82207e929d888c68f804491cec30371
Author: haifxu <[email protected]>
AuthorDate: Mon Apr 8 17:29:31 2024 +0800
[INLONG-9938][Dashboard] Support Agent module and package page (#9939)
---
inlong-dashboard/src/configs/menus/conf.tsx | 8 +
inlong-dashboard/src/configs/routes/conf.ts | 8 +
inlong-dashboard/src/i18n.ts | 4 +
inlong-dashboard/src/ui/locales/cn.json | 22 ++-
inlong-dashboard/src/ui/locales/en.json | 22 ++-
.../src/ui/pages/AgentModule/AgentModuleTag.tsx | 143 ++++++++++++++++
.../src/ui/pages/AgentModule/CreateModal.tsx | 189 +++++++++++++++++++++
.../src/ui/pages/AgentModule/config.tsx | 76 +++++++++
.../src/ui/pages/AgentModule/index.tsx | 82 +++++++++
.../src/ui/pages/AgentPackage/AgentPackageTag.tsx | 143 ++++++++++++++++
.../src/ui/pages/AgentPackage/CreateModal.tsx | 123 ++++++++++++++
.../src/ui/pages/AgentPackage/config.tsx | 80 +++++++++
.../src/ui/pages/AgentPackage/index.tsx | 82 +++++++++
13 files changed, 980 insertions(+), 2 deletions(-)
diff --git a/inlong-dashboard/src/configs/menus/conf.tsx
b/inlong-dashboard/src/configs/menus/conf.tsx
index 29cde12f70..dc660dc62f 100644
--- a/inlong-dashboard/src/configs/menus/conf.tsx
+++ b/inlong-dashboard/src/configs/menus/conf.tsx
@@ -101,6 +101,14 @@ const conf: MenuItemType[] = [
path: '/system',
name: i18n.t('configs.menus.ModuleAudit'),
},
+ {
+ path: '/agentModule',
+ name: i18n.t('configs.menus.agentModule'),
+ },
+ {
+ path: '/agentPackage',
+ name: i18n.t('configs.menus.agentPackage'),
+ },
],
},
];
diff --git a/inlong-dashboard/src/configs/routes/conf.ts
b/inlong-dashboard/src/configs/routes/conf.ts
index a5329e8319..007538d997 100644
--- a/inlong-dashboard/src/configs/routes/conf.ts
+++ b/inlong-dashboard/src/configs/routes/conf.ts
@@ -124,6 +124,14 @@ const conf: RouteProps[] = [
},
],
},
+ {
+ path: '/agentModule',
+ component: () => import('@/ui/pages/AgentModule'),
+ },
+ {
+ path: '/agentPackage',
+ component: () => import('@/ui/pages/AgentPackage'),
+ },
{
component: () => import('@/ui/pages/Error/404'),
},
diff --git a/inlong-dashboard/src/i18n.ts b/inlong-dashboard/src/i18n.ts
index 6a0d7f6de4..0146bf9320 100644
--- a/inlong-dashboard/src/i18n.ts
+++ b/inlong-dashboard/src/i18n.ts
@@ -38,6 +38,8 @@ const resources = {
'configs.menus.TenantManagement': 'Tenant Management',
'configs.menus.SystemOperation': 'Operation',
'configs.menus.ModuleAudit': 'Module audit',
+ 'configs.menus.agentModule': 'Version Management',
+ 'configs.menus.agentPackage': 'Package',
},
},
cn: {
@@ -55,6 +57,8 @@ const resources = {
'configs.menus.TenantManagement': '租户管理',
'configs.menus.SystemOperation': '系统运维',
'configs.menus.ModuleAudit': '模块审计',
+ 'configs.menus.agentModule': '版本管理',
+ 'configs.menus.agentPackage': '安装包',
},
},
};
diff --git a/inlong-dashboard/src/ui/locales/cn.json
b/inlong-dashboard/src/ui/locales/cn.json
index f126a8cedf..69eebbcb5a 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -14,6 +14,7 @@
"basic.Creator": "创建人",
"basic.Modifier": "修改人",
"basic.CreateTime": "创建时间",
+ "basic.ModifyTime": "修改时间",
"basic.Yes": "是",
"basic.No": "否",
"basic.Stop": "停止",
@@ -865,5 +866,24 @@
"pages.ModuleAudit.config.BenchmarkIndicator": "基准指标",
"pages.ModuleAudit.config.ComparativeIndicators": "对比指标",
"pages.ModuleAudit.config.InlongGroupId": "数据流组ID",
- "pages.ModuleAudit.config.InlongStreamId": "数据流ID"
+ "pages.ModuleAudit.config.InlongStreamId": "数据流ID",
+ "pages.ModuleAgent.Agent": "Agent",
+ "pages.ModuleAgent.Installer": "Installer",
+ "pages.ModuleAgent.Create": "新建 Module",
+ "pages.ModuleAgent.Config.Name": "名称",
+ "pages.ModuleAgent.Config.Version": "版本",
+ "pages.ModuleAgent.Config.Package": "安装包",
+ "pages.ModuleAgent.Config.CheckCommand": "检查命令",
+ "pages.ModuleAgent.Config.InstallCommand": "安装命令",
+ "pages.ModuleAgent.Config.StartCommand": "启动命令",
+ "pages.ModuleAgent.Config.StopCommand": "停止命令",
+ "pages.ModuleAgent.Config.UninstallCommand": "卸载命令",
+ "pages.ModuleAgent.Config.Creator": "创建人",
+ "pages.ModuleAgent.Config.Modifier": "修改人",
+ "pages.ModuleAgent.Config.ModifyTime": "修改时间",
+ "pages.PackageAgent.Create": "新建 Package",
+ "pages.PackageAgent.Config.FileName": "文件名称",
+ "pages.PackageAgent.Config.DownloadUrl": "下载地址",
+ "pages.PackageAgent.Config.StoragePath": "存储路径",
+ "pages.PackageAgent.Config.Md5": "MD5"
}
diff --git a/inlong-dashboard/src/ui/locales/en.json
b/inlong-dashboard/src/ui/locales/en.json
index f206b4f47b..06a27fb7e8 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -14,6 +14,7 @@
"basic.Creator": "Creator",
"basic.Modifier": "Modifier",
"basic.CreateTime": "Create time",
+ "basic.ModifyTime": "Modify time",
"basic.Yes": "Yes",
"basic.No": "No",
"basic.Stop": "Stop",
@@ -865,5 +866,24 @@
"pages.ModuleAudit.config.BenchmarkIndicator": "Benchmark indicator",
"pages.ModuleAudit.config.ComparativeIndicators": "Comparative indicator",
"pages.ModuleAudit.config.InlongGroupId": "Inlong group id",
- "pages.ModuleAudit.config.InlongStreamId": "Inlong stream id"
+ "pages.ModuleAudit.config.InlongStreamId": "Inlong stream id",
+ "pages.ModuleAgent.Agent": "Agent",
+ "pages.ModuleAgent.Installer": "Installer",
+ "pages.ModuleAgent.Create": "Create Module",
+ "pages.ModuleAgent.Config.Name": "Name",
+ "pages.ModuleAgent.Config.Version": "Version",
+ "pages.ModuleAgent.Config.Package": "Package",
+ "pages.ModuleAgent.Config.CheckCommand": "Check Command",
+ "pages.ModuleAgent.Config.InstallCommand": "Install Command",
+ "pages.ModuleAgent.Config.StartCommand": "Start Command",
+ "pages.ModuleAgent.Config.StopCommand": "Stop Command",
+ "pages.ModuleAgent.Config.UninstallCommand": "Uninstall Command",
+ "pages.ModuleAgent.Config.Creator": "Creator",
+ "pages.ModuleAgent.Config.Modifier": "Modifier",
+ "pages.ModuleAgent.Config.ModifyTime": "ModifyTime",
+ "pages.PackageAgent.Create": "Create Package",
+ "pages.PackageAgent.Config.FileName": "File Name",
+ "pages.PackageAgent.Config.DownloadUrl": "Download Url",
+ "pages.PackageAgent.Config.Md5": "MD5",
+ "pages.PackageAgent.Config.StoragePath": "Storage Path"
}
diff --git a/inlong-dashboard/src/ui/pages/AgentModule/AgentModuleTag.tsx
b/inlong-dashboard/src/ui/pages/AgentModule/AgentModuleTag.tsx
new file mode 100644
index 0000000000..9dd7789a91
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentModule/AgentModuleTag.tsx
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useEffect, useState } from 'react';
+import { Button, message, Modal } from 'antd';
+import HighTable from '@/ui/components/HighTable';
+import { useRequest } from '@/ui/hooks';
+import { defaultSize } from '@/configs/pagination';
+import { useColumns, getFormContent } from './config';
+import { useLocation } from 'react-router-dom';
+import i18n from '@/i18n';
+import CreateModal from './CreateModal';
+import request from '@/core/utils/request';
+
+interface AgentModalProps {
+ AgentModalType: String;
+}
+
+const Comp: React.FC<AgentModalProps> = ({ AgentModalType }) => {
+ const location = useLocation();
+ const type = location.state || AgentModalType;
+
+ const [query, setQuery] = useState({
+ type: type,
+ keyword: '',
+ pageNum: 1,
+ pageSize: defaultSize,
+ });
+
+ useEffect(() => {
+ setQuery(prev => ({
+ ...prev,
+ type: type,
+ }));
+ }, [type]);
+
+ const [createModal, setCreateModal] = useState<Record<string, unknown>>({
+ open: false,
+ });
+
+ const { data: sourceData = [], run: getList } = useRequest(
+ {
+ url: '/module/list',
+ method: 'POST',
+ data: query,
+ },
+ {
+ refreshDeps: [query],
+ },
+ );
+
+ const pagination = {
+ pageSize: query.pageSize,
+ current: query.pageNum,
+ total: sourceData?.total,
+ };
+
+ const onChange = ({ current: pageNum, pageSize }) => {
+ setQuery(prev => ({
+ ...prev,
+ pageNum,
+ pageSize,
+ }));
+ };
+
+ const onFilter = keyword => {
+ setQuery({
+ ...query,
+ ...keyword,
+ });
+ };
+
+ const openModal = ({ id, type }) => {
+ setCreateModal({ open: true, id, type });
+ };
+
+ const onDelete = async ({ id }) => {
+ Modal.confirm({
+ title: i18n.t('basic.DeleteConfirm'),
+ onOk: async () => {
+ await request({
+ url: `/module/delete/${id}`,
+ method: 'DELETE',
+ });
+ await getList();
+ message.success(i18n.t('basic.DeleteSuccess'));
+ },
+ });
+ };
+
+ const columns = useColumns({ onDelete, openModal });
+
+ return (
+ <>
+ <HighTable
+ filterForm={{
+ content: getFormContent(query),
+ onFilter,
+ }}
+ suffix={
+ <Button type="primary" onClick={() => setCreateModal({ open: true,
type })}>
+ {i18n.t('pages.ModuleAgent.Create')}
+ </Button>
+ }
+ table={{
+ columns: columns,
+ dataSource: sourceData?.list,
+ rowKey: 'id',
+ pagination,
+ onChange,
+ }}
+ />
+
+ <CreateModal
+ {...createModal}
+ open={createModal.open as boolean}
+ onOk={async () => {
+ await getList();
+ setCreateModal({ open: false });
+ }}
+ onCancel={() => setCreateModal({ open: false, type })}
+ />
+ </>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/AgentModule/CreateModal.tsx
b/inlong-dashboard/src/ui/pages/AgentModule/CreateModal.tsx
new file mode 100644
index 0000000000..b9532c1b29
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentModule/CreateModal.tsx
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useMemo } from 'react';
+import { Button, message, Modal } from 'antd';
+import i18n from '@/i18n';
+import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
+import { useRequest, useUpdateEffect } from '@/ui/hooks';
+import request from '@/core/utils/request';
+import { ModalProps } from 'antd/es/modal';
+
+export interface Props extends ModalProps {
+ id?: string;
+ type?: string;
+}
+
+const Comp: React.FC<Props> = ({ id, type, ...modalProps }) => {
+ const [form] = useForm();
+
+ const content = useMemo(() => {
+ return [
+ {
+ type: 'input',
+ label: i18n.t('pages.ModuleAgent.Config.Name'),
+ name: 'name',
+ rules: [{ required: true }],
+ },
+ {
+ type: 'input',
+ label: i18n.t('pages.ModuleAgent.Config.Version'),
+ name: 'version',
+ rules: [{ required: true }],
+ },
+ {
+ type: 'select',
+ label: i18n.t('pages.ModuleAgent.Config.Package'),
+ name: 'packageId',
+ rules: [{ required: true }],
+ props: {
+ showSearch: true,
+ allowClear: true,
+ filterOption: false,
+ options: {
+ requestAuto: true,
+ requestTrigger: ['onOpen', 'onSearch'],
+ requestService: keyword => ({
+ url: '/package/list',
+ method: 'POST',
+ data: {
+ keyword,
+ pageNum: 1,
+ pageSize: 9999,
+ type: type,
+ },
+ }),
+ requestParams: {
+ formatResult: result =>
+ result?.list?.map(item => ({
+ ...item,
+ label: item.fileName,
+ value: item.id,
+ })),
+ },
+ },
+ },
+ },
+ {
+ type: 'textarea',
+ label: i18n.t('pages.ModuleAgent.Config.CheckCommand'),
+ name: 'checkCommand',
+ props: {
+ showCount: true,
+ maxLength: 1000,
+ },
+ },
+ {
+ type: 'textarea',
+ label: i18n.t('pages.ModuleAgent.Config.InstallCommand'),
+ name: 'installCommand',
+ props: {
+ showCount: true,
+ maxLength: 1000,
+ },
+ },
+ {
+ type: 'textarea',
+ label: i18n.t('pages.ModuleAgent.Config.StartCommand'),
+ name: 'startCommand',
+ props: {
+ showCount: true,
+ maxLength: 1000,
+ },
+ },
+ {
+ type: 'textarea',
+ label: i18n.t('pages.ModuleAgent.Config.StopCommand'),
+ name: 'stopCommand',
+ props: {
+ showCount: true,
+ maxLength: 1000,
+ },
+ },
+ {
+ type: 'textarea',
+ label: i18n.t('pages.ModuleAgent.Config.UninstallCommand'),
+ name: 'uninstallCommand',
+ props: {
+ showCount: true,
+ maxLength: 1000,
+ },
+ },
+ ];
+ }, [type]);
+
+ const { data, run: getData } = useRequest(
+ id => ({
+ url: `/module/get/${id}`,
+ }),
+ {
+ manual: true,
+ onSuccess: result => {
+ form.setFieldsValue(result);
+ },
+ },
+ );
+
+ const onOk = async () => {
+ const values = await form.validateFields();
+ const isUpdate = Boolean(id);
+ if (isUpdate) {
+ values.id = id;
+ } else {
+ values.type = type;
+ }
+ await request({
+ url: isUpdate ? '/module/update' : '/module/save',
+ method: 'POST',
+ data: { ...values },
+ });
+ await modalProps?.onOk(values);
+ message.success(i18n.t('basic.OperatingSuccess'));
+ };
+
+ useUpdateEffect(() => {
+ if (modalProps.open) {
+ if (id) {
+ getData(id);
+ }
+ } else {
+ form.resetFields();
+ }
+ }, [modalProps.open]);
+
+ return (
+ <Modal
+ {...modalProps}
+ width={800}
+ title={id ? i18n.t('basic.Edit') : i18n.t('basic.Create')}
+ footer={[
+ <Button key="cancel" onClick={e => modalProps.onCancel(e)}>
+ {i18n.t('basic.Cancel')}
+ </Button>,
+ <Button key="save" type="primary" onClick={onOk}>
+ {i18n.t('basic.Save')}
+ </Button>,
+ ]}
+ >
+ <FormGenerator content={content} form={form} initialValues={id ? data :
{}} useMaxWidth />
+ </Modal>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/AgentModule/config.tsx
b/inlong-dashboard/src/ui/pages/AgentModule/config.tsx
new file mode 100644
index 0000000000..8f6d90a15e
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentModule/config.tsx
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import i18n from '@/i18n';
+import { Button } from 'antd';
+import React from 'react';
+import { timestampFormat } from '@/core/utils';
+
+export const useColumns = ({ onDelete, openModal }) => {
+ const defaultColumns = [
+ {
+ title: i18n.t('pages.ModuleAgent.Config.Name'),
+ dataIndex: 'name',
+ },
+ {
+ title: i18n.t('pages.ModuleAgent.Config.Version'),
+ dataIndex: 'version',
+ },
+ {
+ title: i18n.t('basic.Creator'),
+ dataIndex: 'creator',
+ },
+ {
+ title: i18n.t('basic.Modifier'),
+ dataIndex: 'modifier',
+ },
+ {
+ title: i18n.t('basic.ModifyTime'),
+ dataIndex: 'modifyTime',
+ render: text => text && timestampFormat(text),
+ },
+ ];
+ return defaultColumns.concat([
+ {
+ title: i18n.t('basic.Operating'),
+ dataIndex: 'action',
+ render: (text, record) => (
+ <>
+ <Button type="link" onClick={() => openModal(record)}>
+ {i18n.t('basic.Detail')}
+ </Button>
+ <Button type="link" onClick={() => onDelete(record)}>
+ {i18n.t('basic.Delete')}
+ </Button>
+ </>
+ ),
+ } as any,
+ ]);
+};
+
+export const getFormContent = initialValues => [
+ {
+ type: 'inputsearch',
+ name: 'keyword',
+ initialValue: initialValues.keyword,
+ props: {
+ allowClear: true,
+ },
+ },
+];
diff --git a/inlong-dashboard/src/ui/pages/AgentModule/index.tsx
b/inlong-dashboard/src/ui/pages/AgentModule/index.tsx
new file mode 100644
index 0000000000..65356c3a4f
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentModule/index.tsx
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useState } from 'react';
+import i18n from '@/i18n';
+import AgentModalTag from './AgentModuleTag';
+import { Container, PageContainer } from '@/ui/components/PageContainer';
+import { Card } from 'antd';
+import { useHistory, useParams } from '@/ui/hooks';
+
+const Comp: React.FC = () => {
+ const tabList = [
+ {
+ tab: i18n.t('pages.ModuleAgent.Agent'),
+ key: 'AGENT',
+ content: <AgentModalTag AgentModalType={'AGENT'} />,
+ },
+ {
+ tab: i18n.t('pages.ModuleAgent.Installer'),
+ key: 'INSTALLER',
+ content: <AgentModalTag AgentModalType={'INSTALLER'} />,
+ },
+ ];
+
+ const history = useHistory();
+
+ const { type } = useParams<Record<string, string>>();
+
+ const [module, setModule] = useState(type || tabList[0].key);
+
+ const tabListMap = tabList.reduce(
+ (acc, item) => ({
+ ...acc,
+ [item.key]: item.content,
+ }),
+ {},
+ );
+
+ const onTabsChange = value => {
+ setModule(value);
+ history.push({
+ pathname: `/agentModule`,
+ state: { type: value },
+ });
+ };
+
+ return (
+ <PageContainer useDefaultBreadcrumb={false} useDefaultContainer={false}>
+ <Container>
+ <Card
+ tabList={tabList}
+ activeTabKey={module}
+ onTabChange={key => {
+ onTabsChange(key);
+ }}
+ headStyle={{ border: 'none' }}
+ tabProps={{ size: 'middle' }}
+ >
+ {tabListMap[module]}
+ </Card>
+ </Container>
+ </PageContainer>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/AgentPackage/AgentPackageTag.tsx
b/inlong-dashboard/src/ui/pages/AgentPackage/AgentPackageTag.tsx
new file mode 100644
index 0000000000..4920c3b612
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentPackage/AgentPackageTag.tsx
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useEffect, useState } from 'react';
+import { Button, message, Modal } from 'antd';
+import HighTable from '@/ui/components/HighTable';
+import { useRequest } from '@/ui/hooks';
+import { defaultSize } from '@/configs/pagination';
+import { useColumns, getFormContent } from './config';
+import CreateModal from './CreateModal';
+import i18n from '@/i18n';
+import { useLocation } from 'react-router-dom';
+import request from '@/core/utils/request';
+
+interface AgentPackageProps {
+ AgentPackageType: String;
+}
+
+const Comp: React.FC<AgentPackageProps> = ({ AgentPackageType }) => {
+ const location = useLocation();
+ const type = location.state || AgentPackageType;
+
+ const [query, setQuery] = useState({
+ type: type,
+ keyword: '',
+ pageNum: 1,
+ pageSize: defaultSize,
+ });
+
+ useEffect(() => {
+ setQuery(prev => ({
+ ...prev,
+ type: type,
+ }));
+ }, [type]);
+
+ const [createModal, setCreateModal] = useState<Record<string, unknown>>({
+ open: false,
+ });
+
+ const { data: sourceData = [], run: getList } = useRequest(
+ {
+ url: '/package/list',
+ method: 'POST',
+ data: query,
+ },
+ {
+ refreshDeps: [query],
+ },
+ );
+
+ const pagination = {
+ pageSize: query.pageSize,
+ current: query.pageNum,
+ total: sourceData?.total,
+ };
+
+ const onChange = ({ current: pageNum, pageSize }) => {
+ setQuery(prev => ({
+ ...prev,
+ pageNum,
+ pageSize,
+ }));
+ };
+
+ const onFilter = keyword => {
+ setQuery({
+ ...query,
+ ...keyword,
+ });
+ };
+
+ const openModal = ({ id, type }) => {
+ setCreateModal({ open: true, id, type });
+ };
+
+ const onDelete = async ({ id }) => {
+ Modal.confirm({
+ title: i18n.t('basic.DeleteConfirm'),
+ onOk: async () => {
+ await request({
+ url: `/package/delete/${id}`,
+ method: 'DELETE',
+ });
+ await getList();
+ message.success(i18n.t('basic.DeleteSuccess'));
+ },
+ });
+ };
+
+ const columns = useColumns({ onDelete, openModal });
+
+ return (
+ <>
+ <HighTable
+ filterForm={{
+ content: getFormContent(query),
+ onFilter,
+ }}
+ suffix={
+ <Button type="primary" onClick={() => setCreateModal({ open: true,
type })}>
+ {i18n.t('pages.PackageAgent.Create')}
+ </Button>
+ }
+ table={{
+ columns: columns,
+ dataSource: sourceData?.list,
+ rowKey: 'id',
+ pagination,
+ onChange,
+ }}
+ />
+
+ <CreateModal
+ {...createModal}
+ open={createModal.open as boolean}
+ onOk={async () => {
+ await getList();
+ setCreateModal({ open: false });
+ }}
+ onCancel={() => setCreateModal({ open: false })}
+ />
+ </>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/AgentPackage/CreateModal.tsx
b/inlong-dashboard/src/ui/pages/AgentPackage/CreateModal.tsx
new file mode 100644
index 0000000000..6308ff0b6b
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentPackage/CreateModal.tsx
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useMemo } from 'react';
+import { Button, message, Modal } from 'antd';
+import i18n from '@/i18n';
+import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
+import { useRequest, useUpdateEffect } from '@/ui/hooks';
+import request from '@/core/utils/request';
+import { ModalProps } from 'antd/es/modal';
+
+export interface Props extends ModalProps {
+ // Require when edit
+ id?: string;
+ type?: string;
+}
+
+const Comp: React.FC<Props> = ({ id, type, ...modalProps }) => {
+ const [form] = useForm();
+
+ const content = useMemo(() => {
+ return [
+ {
+ type: 'input',
+ label: i18n.t('pages.PackageAgent.Config.FileName'),
+ name: 'fileName',
+ rules: [{ required: true }],
+ },
+ {
+ type: 'input',
+ label: i18n.t('pages.PackageAgent.Config.DownloadUrl'),
+ name: 'downloadUrl',
+ rules: [{ required: true }],
+ },
+ {
+ type: 'input',
+ label: i18n.t('pages.PackageAgent.Config.Md5'),
+ name: 'md5',
+ rules: [{ required: true }],
+ },
+ {
+ type: 'input',
+ label: i18n.t('pages.PackageAgent.Config.StoragePath'),
+ name: 'storagePath',
+ rules: [{ required: true }],
+ },
+ ];
+ }, []);
+
+ const { data, run: getData } = useRequest(
+ id => ({
+ url: `/package/get/${id}`,
+ }),
+ {
+ manual: true,
+ onSuccess: result => {
+ form.setFieldsValue(result);
+ },
+ },
+ );
+
+ const onOk = async () => {
+ const values = await form.validateFields();
+ const isUpdate = Boolean(id);
+ if (isUpdate) {
+ values.id = id;
+ } else {
+ values.type = type;
+ }
+ await request({
+ url: isUpdate ? '/package/update' : '/package/save',
+ method: 'POST',
+ data: { ...values },
+ });
+ await modalProps?.onOk(values);
+ message.success(i18n.t('basic.OperatingSuccess'));
+ };
+
+ useUpdateEffect(() => {
+ if (modalProps.open) {
+ if (id) {
+ getData(id);
+ }
+ } else {
+ form.resetFields();
+ }
+ }, [modalProps.open]);
+
+ return (
+ <Modal
+ {...modalProps}
+ title={id ? i18n.t('basic.Edit') : i18n.t('basic.Create')}
+ footer={[
+ <Button key="cancel" onClick={e => modalProps.onCancel(e)}>
+ {i18n.t('basic.Cancel')}
+ </Button>,
+ <Button key="save" type="primary" onClick={onOk}>
+ {i18n.t('basic.Save')}
+ </Button>,
+ ]}
+ >
+ <FormGenerator content={content} form={form} initialValues={id ? data :
{}} useMaxWidth />
+ </Modal>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/AgentPackage/config.tsx
b/inlong-dashboard/src/ui/pages/AgentPackage/config.tsx
new file mode 100644
index 0000000000..8b680decf1
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentPackage/config.tsx
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import i18n from '@/i18n';
+import { Button } from 'antd';
+import React from 'react';
+import { timestampFormat } from '@/core/utils';
+
+export const useColumns = ({ onDelete, openModal }) => {
+ const defaultColumns = [
+ {
+ title: i18n.t('pages.PackageAgent.Config.FileName'),
+ dataIndex: 'fileName',
+ },
+ {
+ title: i18n.t('pages.PackageAgent.Config.StoragePath'),
+ dataIndex: 'storagePath',
+ },
+ {
+ title: i18n.t('pages.PackageAgent.Config.DownloadUrl'),
+ dataIndex: 'downloadUrl',
+ },
+ {
+ title: i18n.t('basic.Creator'),
+ dataIndex: 'creator',
+ },
+ {
+ title: i18n.t('basic.Modifier'),
+ dataIndex: 'modifier',
+ },
+ {
+ title: i18n.t('basic.ModifyTime'),
+ dataIndex: 'modifyTime',
+ render: text => text && timestampFormat(text),
+ },
+ ];
+ return defaultColumns.concat([
+ {
+ title: i18n.t('basic.Operating'),
+ dataIndex: 'action',
+ render: (text, record) => (
+ <>
+ <Button type="link" onClick={() => openModal(record)}>
+ {i18n.t('basic.Detail')}
+ </Button>
+ <Button type="link" onClick={() => onDelete(record)}>
+ {i18n.t('basic.Delete')}
+ </Button>
+ </>
+ ),
+ } as any,
+ ]);
+};
+
+export const getFormContent = initialValues => [
+ {
+ type: 'inputsearch',
+ name: 'keyword',
+ initialValue: initialValues.keyword,
+ props: {
+ allowClear: true,
+ },
+ },
+];
diff --git a/inlong-dashboard/src/ui/pages/AgentPackage/index.tsx
b/inlong-dashboard/src/ui/pages/AgentPackage/index.tsx
new file mode 100644
index 0000000000..6def0b008b
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/AgentPackage/index.tsx
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useState } from 'react';
+import { Card } from 'antd';
+import { useHistory, useParams } from '@/ui/hooks';
+import { Container, PageContainer } from '@/ui/components/PageContainer';
+import i18n from '@/i18n';
+import AgentPackageTag from '@/ui/pages/AgentPackage/AgentPackageTag';
+
+const Comp: React.FC = () => {
+ const tabList = [
+ {
+ tab: i18n.t('pages.ModuleAgent.Agent'),
+ key: 'AGENT',
+ content: <AgentPackageTag AgentPackageType={'AGENT'} />,
+ },
+ {
+ tab: i18n.t('pages.ModuleAgent.Installer'),
+ key: 'INSTALLER',
+ content: <AgentPackageTag AgentPackageType={'INSTALLER'} />,
+ },
+ ];
+
+ const history = useHistory();
+
+ const { type } = useParams<Record<string, string>>();
+
+ const [module, setModule] = useState(type || tabList[0].key);
+
+ const tabListMap = tabList.reduce(
+ (acc, item) => ({
+ ...acc,
+ [item.key]: item.content,
+ }),
+ {},
+ );
+
+ const onTabsChange = value => {
+ setModule(value);
+ history.push({
+ pathname: `/agentPackage`,
+ state: { type: value },
+ });
+ };
+
+ return (
+ <PageContainer useDefaultBreadcrumb={false} useDefaultContainer={false}>
+ <Container>
+ <Card
+ tabList={tabList}
+ activeTabKey={module}
+ onTabChange={key => {
+ onTabsChange(key);
+ }}
+ headStyle={{ border: 'none' }}
+ tabProps={{ size: 'middle' }}
+ >
+ {tabListMap[module]}
+ </Card>
+ </Container>
+ </PageContainer>
+ );
+};
+
+export default Comp;