From 3aa4a15d9fa19e037f5e10680482adb2a8d223bf Mon Sep 17 00:00:00 2001 From: yuanganping Date: Mon, 27 Dec 2021 18:07:26 +0800 Subject: [PATCH 01/10] feat: add page Signed-off-by: yuanganping --- src/i18n/en.json | 4 ++ src/i18n/zh.json | 7 ++ src/pages/Home.tsx/routes.tsx | 5 ++ src/pages/Setting/components/ThirdAccount.tsx | 30 +++++++++ src/pages/Setting/index.tsx | 41 ++++++++++++ src/pages/Setting/styled-component.tsx | 65 +++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 src/pages/Setting/components/ThirdAccount.tsx create mode 100644 src/pages/Setting/index.tsx create mode 100644 src/pages/Setting/styled-component.tsx diff --git a/src/i18n/en.json b/src/i18n/en.json index e8d47bb..fc31856 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -525,5 +525,9 @@ "user": "User" } } + }, + "settings": { + "settingCenter": "Configuration Center", + "thirdCount": "Third party account configuration" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 8396eff..0796fb4 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -526,5 +526,12 @@ "user": "普通用户" } } + }, + "settings": { + "settingCenter": "配置中心", + "thirdCount": "第三方账号配置", + "configService": "Configuration Services", + "unallocated": "未配置", + "configured": "已配置" } } diff --git a/src/pages/Home.tsx/routes.tsx b/src/pages/Home.tsx/routes.tsx index b76b429..5095776 100644 --- a/src/pages/Home.tsx/routes.tsx +++ b/src/pages/Home.tsx/routes.tsx @@ -7,6 +7,7 @@ import EnvList from '../EnvList'; import DevSpace from '../DevSpace'; import DevSpaceOperation from '../DevSpace/components/DevspaceOperation'; import MeshSpace from '../DevSpace/components/MeshSpace'; +import Setting from '../Setting'; export const routes = [ { path: '/dashboard/overview', @@ -51,4 +52,8 @@ export const routes = [ path: '/dashboard/devspace/mesh-space', component: MeshSpace, }, + { + path: '/dashboard/setting', + component: Setting, + }, ]; diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx new file mode 100644 index 0000000..cbf9b3e --- /dev/null +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { ThirdAccountWrap } from '../styled-component'; +import { Button } from 'antd'; +import { useTranslation } from 'react-i18next'; + +const ThirdAccount = ({ status }: { status: string }) => { + const { t } = useTranslation(); + return ( + +
+
+
+
+ LDAP + {t(`settings.${status}`)} +
+
+ + +
+
+
+
+ +
+
+ ); +}; + +export default ThirdAccount; diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx new file mode 100644 index 0000000..f4560dc --- /dev/null +++ b/src/pages/Setting/index.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { SettingWrap } from './styled-component'; +import ThirdAccount from './components/ThirdAccount'; + +const CONFIG_MENU_LIST = ['thirdCount']; + +const Settings = () => { + const { t } = useTranslation(); + const [currentIndex, setCurrentIndex] = useState(0); + + return ( + +
+
{t('settings.settingCenter')}
+
    + {CONFIG_MENU_LIST.map((item, index) => { + return ( +
  • setCurrentIndex(index)} + key={index} + className={`menu-list-item${ + currentIndex === index ? ' active' : '' + }`} + > + {t(`settings.${item}`)} +
  • + ); + })} +
+
+
+
{t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
+ +
+
+ ); +}; + +export default Settings; diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx new file mode 100644 index 0000000..8cfc346 --- /dev/null +++ b/src/pages/Setting/styled-component.tsx @@ -0,0 +1,65 @@ +import styled from 'styled-components'; + +export const SettingWrap = styled.div` + display: flex; + min-height: 604px; + background: #ffffff; + + .menu { + width: 200px; + border-right: 1px solid #dae1e8; + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + color: #36435c; + + .title { + padding: 16px 0 16px 20px; + } + + &-list { + font-size: 14px; + + &-item { + height: 40px; + padding: 0 20px; + display: flex; + align-items: center; + cursor: pointer; + + &.active, + &:hover { + color: #0066ff; + border-right: 2px solid #0066ff; + background: #f3f6fa; + } + } + } + } + + .content { + flex: 1; + padding: 0 20px; + + .title { + padding: 16px 0; + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + color: #36435c; + } + } +`; + +export const ThirdAccountWrap = styled.div` + display: flex; + padding: 30px 20px; + border: 1px solid #dae1e8; + border-radius: 4px; + justify-content: space-between; + align-items: center; + + .left { + display: flex; + } +`; From 995bf36794704a93f4d5bbad2c80a5f55cae5030 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Tue, 28 Dec 2021 10:39:49 +0800 Subject: [PATCH 02/10] fix: change desc Signed-off-by: yuanganping --- src/i18n/en.json | 6 +++- src/i18n/zh.json | 6 ++-- src/pages/Setting/components/ThirdAccount.tsx | 8 ++--- src/pages/Setting/index.tsx | 4 +-- src/pages/Setting/styled-component.tsx | 34 ++++++++++++++++++- 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index fc31856..d631e94 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -528,6 +528,10 @@ }, "settings": { "settingCenter": "Configuration Center", - "thirdCount": "Third party account configuration" + "thirdAccount": "Third party account configuration", + "unallocated": "unallocated", + "configured": "configured", + "thirdAccountDesc": "After configuring LDAP, you can import LDAP member directories to achieve single sign-on to Nocalhost for LDAP accounts. see the", + "helpDocs": "help documentation" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 0796fb4..378cca1 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -529,9 +529,11 @@ }, "settings": { "settingCenter": "配置中心", - "thirdCount": "第三方账号配置", + "thirdAccount": "第三方账号配置", "configService": "Configuration Services", "unallocated": "未配置", - "configured": "已配置" + "configured": "已配置", + "thirdAccountDesc": "配置 LDAP 后,可导入 LDAP 成员目录,实现 LDAP 账号单点登录 Nocalhost。查阅 ", + "helpDocs": "帮助文档" } } diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx index cbf9b3e..c8849c2 100644 --- a/src/pages/Setting/components/ThirdAccount.tsx +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -6,17 +6,17 @@ import { useTranslation } from 'react-i18next'; const ThirdAccount = ({ status }: { status: string }) => { const { t } = useTranslation(); return ( - +
-
+
LDAP {t(`settings.${status}`)}
- - + {t('settings.thirdAccountDesc')} + {t('settings.helpDocs')}
diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx index f4560dc..61cb98d 100644 --- a/src/pages/Setting/index.tsx +++ b/src/pages/Setting/index.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { SettingWrap } from './styled-component'; import ThirdAccount from './components/ThirdAccount'; -const CONFIG_MENU_LIST = ['thirdCount']; +const CONFIG_MENU_LIST = ['thirdAccount']; const Settings = () => { const { t } = useTranslation(); @@ -32,7 +32,7 @@ const Settings = () => {
{t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
- +
); diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx index 8cfc346..b242061 100644 --- a/src/pages/Setting/styled-component.tsx +++ b/src/pages/Setting/styled-component.tsx @@ -51,7 +51,11 @@ export const SettingWrap = styled.div` } `; -export const ThirdAccountWrap = styled.div` +interface IThirdAccount { + status: string; +} + +export const ThirdAccountWrap = styled.div` display: flex; padding: 30px 20px; border: 1px solid #dae1e8; @@ -61,5 +65,33 @@ export const ThirdAccountWrap = styled.div` .left { display: flex; + + .logo { + width: 40px; + height: 40px; + } + + .content { + .sub-title { + color: #202d40; + display: flex; + align-items: center; + + .status { + margin-left: 8px; + padding: 4px; + font-size: 12px; + border-radius: 2px; + background: ${(props) => + props.status === 'unAllocated' ? '#ffac52' : '#4ac285'}; + } + } + + .desc { + margin-top: 10px; + font-size: 12px; + color: #79879c; + } + } } `; From 14a8ed9319b84c8babb7df2bea376255602b897f Mon Sep 17 00:00:00 2001 From: yuanganping Date: Wed, 29 Dec 2021 17:01:08 +0800 Subject: [PATCH 03/10] feat: add setting page Signed-off-by: yuanganping --- package.json | 2 +- src/components/LabelWithHelp/index.tsx | 46 ++++ src/components/MainHeader/index.tsx | 11 + .../MainHeader/style-components.tsx | 4 + src/i18n/en.json | 17 +- src/i18n/zh.json | 28 ++- src/images/bg_left_select.svg | 27 +++ src/images/bg_left_unselect.svg | 27 +++ src/images/bg_right_select.svg | 27 +++ src/images/bg_right_unselect.svg | 27 +++ src/images/icon/icon_arrow_right.svg | 15 ++ src/images/icon/icon_ldap.svg | 16 ++ src/images/icon/icon_set_elected.svg | 12 ++ src/images/icon/icon_set_normal.svg | 12 ++ src/images/icon_set_elected.svg | 12 ++ src/images/icon_set_normal.svg | 12 ++ .../components/MeshSpace/BaseSpace.tsx | 2 - .../Setting/components/ConfigService.tsx | 200 ++++++++++++++++++ src/pages/Setting/components/ThirdAccount.tsx | 20 +- src/pages/Setting/index.tsx | 87 +++++--- src/pages/Setting/styled-component.tsx | 185 +++++++++++++++- src/types.ts | 10 - src/types/index.ts | 36 ++++ 23 files changed, 788 insertions(+), 47 deletions(-) create mode 100644 src/components/LabelWithHelp/index.tsx create mode 100644 src/images/bg_left_select.svg create mode 100644 src/images/bg_left_unselect.svg create mode 100644 src/images/bg_right_select.svg create mode 100644 src/images/bg_right_unselect.svg create mode 100644 src/images/icon/icon_arrow_right.svg create mode 100644 src/images/icon/icon_ldap.svg create mode 100644 src/images/icon/icon_set_elected.svg create mode 100644 src/images/icon/icon_set_normal.svg create mode 100644 src/images/icon_set_elected.svg create mode 100644 src/images/icon_set_normal.svg create mode 100644 src/pages/Setting/components/ConfigService.tsx delete mode 100644 src/types.ts diff --git a/package.json b/package.json index 7aa0d82..429e4af 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "whatwg-fetch": "^3.6.2", "yarn": "^1.22.11" }, - "proxy": "http://127.0.0.1:9080/", + "proxy": "http://10.94.100.87:8080/", "scripts": { "start": "GIT_COMMIT_SHA=`git describe --match=NeVeRmAtCh --always --abbrev=40 --dirty` ./env.sh && cp env.js ./public/ && react-app-rewired start", "build": "react-app-rewired build", diff --git a/src/components/LabelWithHelp/index.tsx b/src/components/LabelWithHelp/index.tsx new file mode 100644 index 0000000..a3a86fb --- /dev/null +++ b/src/components/LabelWithHelp/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import CommonIcon from '../CommonIcon'; +import { ReactComponent as IconHelp } from '../../images/icon/icon_label_query.svg'; +import styled from 'styled-components'; + +const LabelWrap = styled.div` + display: flex; + align-items: center; + position: relative; + + .help { + position: absolute; + display: flex; + align-items: center; + top: 50%; + left: 100%; + transform: translate(12px, -50%); + + svg { + &:hover { + path { + fill: #36435c; + } + } + } + } +`; + +interface IProp { + label: string; + tip: string; +} + +const LabelWithHelp = ({ label, tip }: IProp) => { + return ( + +
{label}
+
+ +
+
+ ); +}; + +export default LabelWithHelp; diff --git a/src/components/MainHeader/index.tsx b/src/components/MainHeader/index.tsx index 11a5823..1053912 100644 --- a/src/components/MainHeader/index.tsx +++ b/src/components/MainHeader/index.tsx @@ -52,6 +52,8 @@ import { ReactComponent as IconUser } from '../../images/icon/icon_user.svg'; import { ReactComponent as IconTip } from '../../images/icon/icon_label_tips.svg'; import { ReactComponent as IconClose } from '../../images/icon/icon_close.svg'; import { ReactComponent as IconLink } from '../../images/icon/icon_external_link.svg'; +import { ReactComponent as IconSetNormal } from '../../images/icon/icon_set_normal.svg'; +import { ReactComponent as IconSetActive } from '../../images/icon/icon_set_elected.svg'; import ImageVersionInfo from '../../images/icon_logoWords.svg'; import AddCluster from '../../components/AddCluster'; @@ -285,6 +287,15 @@ function MainHeader() { style={{ fontSize: '24px' }} > +
history.push('/dashboard/setting')}> + +
+ + bg_left_select + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/images/bg_left_unselect.svg b/src/images/bg_left_unselect.svg new file mode 100644 index 0000000..f3b1e3e --- /dev/null +++ b/src/images/bg_left_unselect.svg @@ -0,0 +1,27 @@ + + + bg_left_unselect + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/images/bg_right_select.svg b/src/images/bg_right_select.svg new file mode 100644 index 0000000..677d11e --- /dev/null +++ b/src/images/bg_right_select.svg @@ -0,0 +1,27 @@ + + + bg_right_select + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/images/bg_right_unselect.svg b/src/images/bg_right_unselect.svg new file mode 100644 index 0000000..cfb1625 --- /dev/null +++ b/src/images/bg_right_unselect.svg @@ -0,0 +1,27 @@ + + + bg_right_unselect + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon/icon_arrow_right.svg b/src/images/icon/icon_arrow_right.svg new file mode 100644 index 0000000..cf241e5 --- /dev/null +++ b/src/images/icon/icon_arrow_right.svg @@ -0,0 +1,15 @@ + + + arrow_right + + + + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon/icon_ldap.svg b/src/images/icon/icon_ldap.svg new file mode 100644 index 0000000..debaa75 --- /dev/null +++ b/src/images/icon/icon_ldap.svg @@ -0,0 +1,16 @@ + + + icon_ldap + + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon/icon_set_elected.svg b/src/images/icon/icon_set_elected.svg new file mode 100644 index 0000000..fd0c52d --- /dev/null +++ b/src/images/icon/icon_set_elected.svg @@ -0,0 +1,12 @@ + + + icon_set_elected + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon/icon_set_normal.svg b/src/images/icon/icon_set_normal.svg new file mode 100644 index 0000000..4999ee3 --- /dev/null +++ b/src/images/icon/icon_set_normal.svg @@ -0,0 +1,12 @@ + + + icon_set_normal + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon_set_elected.svg b/src/images/icon_set_elected.svg new file mode 100644 index 0000000..fd0c52d --- /dev/null +++ b/src/images/icon_set_elected.svg @@ -0,0 +1,12 @@ + + + icon_set_elected + + + + + + + + + \ No newline at end of file diff --git a/src/images/icon_set_normal.svg b/src/images/icon_set_normal.svg new file mode 100644 index 0000000..4999ee3 --- /dev/null +++ b/src/images/icon_set_normal.svg @@ -0,0 +1,12 @@ + + + icon_set_normal + + + + + + + + + \ No newline at end of file diff --git a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx index 34d70d5..210ea31 100644 --- a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx +++ b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx @@ -20,9 +20,7 @@ import { ReactComponent as WayLine } from '../../../../images/mesh-icon/way1.svg import { ReactComponent as BlueArrow } from '../../../../images/mesh-icon/arrow.svg'; import { ReactComponent as IconTracingHeader } from '../../../../images/icon/icon_normal_tracingHeaders.svg'; import { ReactComponent as IconActiveTracingHeader } from '../../../../images/icon/icon_active_tracingHeaders.svg'; - import CommonIcon from '../../../../components/CommonIcon'; - import { ReactComponent as IconPath } from '../../../../images/mesh-icon/icon_path.svg'; import { windowAnimationStartHandle, diff --git a/src/pages/Setting/components/ConfigService.tsx b/src/pages/Setting/components/ConfigService.tsx new file mode 100644 index 0000000..d76f9eb --- /dev/null +++ b/src/pages/Setting/components/ConfigService.tsx @@ -0,0 +1,200 @@ +import React, { useEffect, useState } from 'react'; + +import { ConfigServiceWrap } from '../styled-component'; +import { useTranslation } from 'react-i18next'; +import { Form, Input, Radio, Button } from 'antd'; +import { ConfigInfo } from '../../../types'; +import LabelWithHelp from '../../../components/LabelWithHelp'; +import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; +import { ReactComponent as IconArrowRight } from '../../../images/icon/icon_arrow_right.svg'; + +interface IProp { + configData: ConfigInfo | null; + onClose: () => void; +} + +const ConfigService = ({ configData, onClose }: IProp) => { + const { t } = useTranslation(); + const [currentStep, setCurrentStep] = useState(configData ? 2 : 1); + const [folded, setFolded] = useState(true); + const [form] = Form.useForm(); + + useEffect(() => { + if (configData) { + form.setFieldsValue({ + server: configData?.server, + bind_dn: configData?.bind_dn, + password: '******', + security: configData?.tls ? 'tls' : configData?.md5 ? 'md5' : '', + base_dn: configData?.base_dn, + filter: configData?.filter, + email_attr: configData?.email_attr, + user_name_attr: configData?.user_name_attr, + admin_base_dn: configData?.admin_base_dn, + admin_filter: configData?.admin_filter, + }); + } + }, []); + + const handleNextStep = () => { + setCurrentStep(2); + }; + + return ( + +
{t('settings.configDesc')}
+
+
+
1
+
+
{t('settings.serverConfig')}
+
{t('settings.serverConfigTip')}
+
+
+
+
2
+
+
{t('settings.ruleConfig')}
+
{t('settings.ruleConfigTip')}
+
+
+
+
+
+ {currentStep === 1 && ( + <> + + + + + + + + + + + + Enable TLS + Enable Digest MD5 Bind + + + + )} + + {currentStep === 2 && ( + <> + + } + name="email_attr" + required={true} + > + + + + + + +
+
{t('settings.memberRule')}
+ + + + + + +
+
Example: (填完至少基础 DN+Attr 实时获取一个)
+
DN: - -
+
Email: - -
+
Username: - -
+
+
+ +
+
setFolded((curr) => !curr)} + > + {folded ? : } + +
+ {!folded && ( +
+
{t('settings.adminRule')}
+ + + + + + +
+
Example: (填完至少基础 DN+Attr 实时获取一个)
+
DN: - -
+
Email: - -
+
Username: - -
+
+
+ )} +
+ + )} +
+
+
+ + {currentStep === 1 && ( + + )} + {currentStep === 2 && ( + <> + + + + )} +
+
+ ); +}; + +export default ConfigService; diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx index c8849c2..a3abed2 100644 --- a/src/pages/Setting/components/ThirdAccount.tsx +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -3,16 +3,26 @@ import { ThirdAccountWrap } from '../styled-component'; import { Button } from 'antd'; import { useTranslation } from 'react-i18next'; -const ThirdAccount = ({ status }: { status: string }) => { +import { ReactComponent as IconLdap } from '../../../images/icon/icon_ldap.svg'; +import Icon from '@ant-design/icons'; + +interface ThirdAccountProp { + status: string; + showConfig: () => void; +} + +const ThirdAccount = ({ status, showConfig }: ThirdAccountProp) => { const { t } = useTranslation(); return (
-
+
+ +
LDAP - {t(`settings.${status}`)} +
{t(`settings.${status}`)}
{t('settings.thirdAccountDesc')} @@ -21,7 +31,9 @@ const ThirdAccount = ({ status }: { status: string }) => {
- +
); diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx index 61cb98d..2baaa13 100644 --- a/src/pages/Setting/index.tsx +++ b/src/pages/Setting/index.tsx @@ -1,40 +1,77 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { SettingWrap } from './styled-component'; import ThirdAccount from './components/ThirdAccount'; +import ConfigService from './components/ConfigService'; +import { Modal } from 'antd'; +import HTTP from '../../api/fetch'; +import { ConfigInfo } from '../../types'; const CONFIG_MENU_LIST = ['thirdAccount']; const Settings = () => { const { t } = useTranslation(); const [currentIndex, setCurrentIndex] = useState(0); + const [showModal, setShowModal] = useState(false); + const [status, setStatus] = useState<'unallocated' | 'configured'>('unallocated'); + const [configData, setConfigData] = useState(null); + + const showConfig = () => { + setShowModal(true); + }; + + const getConfig = async () => { + const response = await HTTP.get('/ldap/config'); + if (response.code === 0) { + const { data } = response; + if (data) { + setStatus('configured'); + setConfigData(data); + } + } + }; + + useEffect(() => { + getConfig(); + }, []); return ( - -
-
{t('settings.settingCenter')}
-
    - {CONFIG_MENU_LIST.map((item, index) => { - return ( -
  • setCurrentIndex(index)} - key={index} - className={`menu-list-item${ - currentIndex === index ? ' active' : '' - }`} - > - {t(`settings.${item}`)} -
  • - ); - })} -
-
-
-
{t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
- -
-
+ <> + +
+
{t('settings.settingCenter')}
+
    + {CONFIG_MENU_LIST.map((item, index) => { + return ( +
  • setCurrentIndex(index)} + key={index} + className={`menu-list-item${ + currentIndex === index ? ' active' : '' + }`} + > + {t(`settings.${item}`)} +
  • + ); + })} +
+
+
+
{t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
+ +
+
+ setShowModal(false)} + footer={null} + > + setShowModal(false)} /> + + ); }; diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx index b242061..39197c5 100644 --- a/src/pages/Setting/styled-component.tsx +++ b/src/pages/Setting/styled-component.tsx @@ -1,4 +1,8 @@ import styled from 'styled-components'; +import step1active from '../../images/bg_left_select.svg'; +import step1normal from '../../images/bg_left_unselect.svg'; +import step2active from '../../images/bg_right_select.svg'; +import step2normal from '../../images/bg_right_unselect.svg'; export const SettingWrap = styled.div` display: flex; @@ -55,6 +59,10 @@ interface IThirdAccount { status: string; } +interface IConfigService { + step: number; +} + export const ThirdAccountWrap = styled.div` display: flex; padding: 30px 20px; @@ -65,6 +73,7 @@ export const ThirdAccountWrap = styled.div` .left { display: flex; + align-items: center; .logo { width: 40px; @@ -76,14 +85,21 @@ export const ThirdAccountWrap = styled.div` color: #202d40; display: flex; align-items: center; + font-family: PingFangSC-Semibold; + font-weight: 600; + font-size: 16px; .status { + height: 18px; + padding: 0 4px; + display: flex; + align-items: center; margin-left: 8px; - padding: 4px; font-size: 12px; + color: #ffffff; border-radius: 2px; background: ${(props) => - props.status === 'unAllocated' ? '#ffac52' : '#4ac285'}; + props.status === 'unallocated' ? '#ffac52' : '#4ac285'}; } } @@ -95,3 +111,168 @@ export const ThirdAccountWrap = styled.div` } } `; + +export const ConfigServiceWrap = styled.div` + display: block; + overflow: hidden; + + .desc { + font-family: PingFangSC-Regular; + font-size: 14px; + font-weight: normal; + color: #79879c; + } + + .progress { + height: 68px; + margin-top: 20px; + display: flex; + align-items: center; + + .step { + height: 68px; + width: 50%; + padding: 14px; + flex-basis: 50%; + display: flex; + position: relative; + background-size: cover; + + &::after { + content: ''; + width: 0; + height: 0; + display: block; + position: absolute; + top: 100%; + left: 50%; + border: 10px solid transparent; + border-bottom: 10px solid #f9fbfd; + transform: translate(-10px, -10px); + } + + &:nth-child(1) { + background: url(${(props) => (props.step === 1 ? step1active : step1normal)}) + no-repeat; + position: absolute; + z-index: 10; + + &::after { + visibility: ${(props) => (props.step === 1 ? 'visible' : 'hidden')}; + } + } + + &:nth-child(2) { + width: 314px; + position: absolute; + padding-left: 40px; + left: 50%; + background: url(${(props) => (props.step === 2 ? step2active : step2normal)}) + no-repeat; + + &::after { + visibility: ${(props) => (props.step === 2 ? 'visible' : 'hidden')}; + } + } + + .icon { + height: 24px; + width: 24px; + display: flex; + justify-content: center; + align-items: center; + background: rgb(239, 244, 249); + border: 2px solid rgb(255, 255, 255); + box-shadow: 0px 3px 8px 0px rgba(54, 67, 92, 0.2); + border-radius: 50%; + font-family: PingFangSC-Semibold; + font-size: 12px; + font-weight: 600; + color: #b6c2cd; + + &.active { + background: rgb(0, 128, 255); + border: 2px solid rgb(255, 255, 255); + box-shadow: 0px 3px 8px 0px rgba(0, 128, 255, 0.3); + color: #ffffff; + } + } + + .content { + margin-left: 6px; + + .title { + color: rgb(32, 45, 64); + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + } + + .desc { + color: rgb(121, 135, 156); + font-family: PingFangSC-Regular; + font-size: 12px; + } + } + } + } + + .form-wrap { + margin-top: 10px; + padding: 20px 10px; + background: #f9fbfd; + font-size: 14px; + + .member-rule { + padding: 20px 10px 20px 0; + position: relative; + min-height: 184px; + border-radius: 4px; + border: 1px solid rgb(218, 225, 232); + + .rule-label { + position: absolute; + background: #f9fbfd; + top: 0; + left: 0; + transform: translate(10px, -50%); + font-family: PingFangSC-Semibold; + font-size: 14px; + font-weight: 600; + } + + .rule-tip { + padding: 0 10px; + font-size: 12px; + color: #79879c; + } + } + + .advance { + &-setting { + margin-top: 20px; + } + + &-label { + margin-bottom: 20px; + display: flex; + align-items: center; + cursor: pointer; + } + } + + .ant-form-item-label { + padding-left: 10px; + } + } + + .btn-box { + margin-top: 24px; + display: flex; + justify-content: flex-end; + } + + .ant-form-item-control-input { + box-shadow: none; + } +`; diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 947567a..0000000 --- a/src/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface LoginToken { - email: string; - exp: number; - iat: number; - is_admin: number; - nbf: number; - user_id: number; - username: string; - uuid: string; -} diff --git a/src/types/index.ts b/src/types/index.ts index 062aa0c..e650c4c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,13 @@ +export interface LoginToken { + email: string; + exp: number; + iat: number; + is_admin: number; + nbf: number; + user_id: number; + username: string; + uuid: string; +} export interface ClusterItemInfo { id: number; info: string; @@ -18,3 +28,29 @@ export interface ClusterItemInfo { used: number; }[]; } + +export interface ConfigInfo { + admin_base_dn: string; + admin_filter: string; + base_dn: string; + bind_dn: string; + costs: number; + created_at: string; + deletes: number; + email_attr: string; + enable: number; + entries: number; + fails: number; + filter: string; + id: number; + inserts: number; + last_sync_err_msg: string; + ldap_gen: number; + md5: string; + password: string; + server: string; + sync_protection_ts: number; + tls: string; + updates: number; + user_name_attr: string; +} From 125dbbdd2da91335da9bee0a891b01a7e043f824 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Fri, 31 Dec 2021 13:35:36 +0800 Subject: [PATCH 04/10] feat: add ldap Signed-off-by: yuanganping --- package.json | 2 +- src/components/DeleteModal/reset.less | 4 + src/i18n/en.json | 23 ++- src/i18n/zh.json | 15 +- src/images/bg_left_select.svg | 28 +--- src/images/bg_left_unselect.svg | 28 +--- src/images/bg_right_select.svg | 28 +--- src/images/bg_right_unselect.svg | 28 +--- src/images/icon/icon_arrow_right.svg | 16 +-- src/images/icon/icon_ldap.svg | 17 +-- src/images/icon/icon_set_elected.svg | 13 +- src/images/icon/icon_set_normal.svg | 13 +- src/images/icon_set_elected.svg | 13 +- src/images/icon_set_normal.svg | 13 +- .../Setting/components/ConfigService.tsx | 133 ++++++++++++++++-- src/pages/Setting/components/ThirdAccount.tsx | 92 ++++++++++-- src/pages/Setting/index.tsx | 41 +++++- src/pages/Setting/styled-component.tsx | 39 +++++ src/types/index.ts | 4 +- 19 files changed, 328 insertions(+), 222 deletions(-) diff --git a/package.json b/package.json index 429e4af..7aa0d82 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "whatwg-fetch": "^3.6.2", "yarn": "^1.22.11" }, - "proxy": "http://10.94.100.87:8080/", + "proxy": "http://127.0.0.1:9080/", "scripts": { "start": "GIT_COMMIT_SHA=`git describe --match=NeVeRmAtCh --always --abbrev=40 --dirty` ./env.sh && cp env.js ./public/ && react-app-rewired start", "build": "react-app-rewired build", diff --git a/src/components/DeleteModal/reset.less b/src/components/DeleteModal/reset.less index a2c1669..d5a560d 100644 --- a/src/components/DeleteModal/reset.less +++ b/src/components/DeleteModal/reset.less @@ -1,5 +1,9 @@ .queryLogo { svg { fill: #f4ce5d; + + path { + fill: #faad14; + } } } diff --git a/src/i18n/en.json b/src/i18n/en.json index 606a912..de8b22a 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -80,7 +80,8 @@ "copy": "Copy successfully", "upgrade": "Nocalhost Server Dashboard version {{latest}} has been released. Your current version is {{current}}.", "link": "Click to view updated guidelines", - "release": "Version {{version}} has been released," + "release": "Version {{version}} has been released,", + "success": "Operation success" }, "bt": { "delete": "Delete", @@ -547,6 +548,24 @@ "serverAddrPh": "Please enter the server address corp.com,127.0.0.1", "adminAccountPh": "Please enter the administrator account ", "adminPwdPh": "Please enter the administrator password ", - "finishAndSync": "Finish and sync" + "finishAndSync": "Finish and sync", + "emailAttr": "Mail attr", + "emailAttrPh": "Please enter the property name of the mail ", + "usernameAttr": "Username attr", + "usernameAttrPh": "Please enter the attribute name of the user name, e.g. OpenLdap Fill in the uid ", + "memberRule": "Membership Rules", + "baseDn": "Basic DN", + "selector": "Filters", + "advanceSetting": "Advanced Configuration", + "advanceSettingTip": "Users who meet this base DN and filter will be imported as an administrator user group.", + "adminRule": "Admin Rules", + "syncData": "Synchronize data", + "editConfig": "Edit Config", + "modifyConfig": "Modify", + "deleteConfig": "Delete", + "emailAttrTip": "If the email provided is not an email, Nocalhost will automatically splice it as ${attr}@nocalhost.dev, the domain part must meet the LDH rules, otherwise the single entry will not be synchronized, if the database already has this email, the mapping relationship will be established directly, if the email provided is duplicated, the data may be misplaced", + "usernameAttrTip": "If it is not filled in or not obtained, the domain part of the email will be taken as the username when importing, and the part of the email that exceeds 20 characters will be truncated; if the user already exists and is not filled in or not obtained, the original username will not be updated.", + "exampleTip": "(Fill out at least the base DN+Attr to get one in real time)", + "deleteConfirm": "Is it confirmed that you want to delete the configuration" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 3240d79..40c28b6 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -90,7 +90,8 @@ "copy": "复制成功", "upgrade": "Nocalhost Server Dashboard 版本 {{latest}} 已发布,你现在的版本是 {{current}}。", "link": "点击查看更新指引", - "release": "Version {{version}} 已发布," + "release": "Version {{version}} 已发布,", + "success": "操作成功" }, "bt": { "delete": "删除", @@ -557,7 +558,15 @@ "baseDn": "基础 DN", "selector": "筛选器", "advanceSetting": "高级配置", - "advanceSettingTip": "xxxx", - "adminRule": "管理员规则" + "advanceSettingTip": "满足此 基础 DN 和 筛选器 的用户将会被导入为 管理员用户组。", + "adminRule": "管理员规则", + "syncData": "同步数据", + "editConfig": "编辑配置", + "modifyConfig": "修改配置", + "deleteConfig": "删除配置", + "emailAttrTip": "如果提供的不是一个 email,则 Nocalhost 将自动为其拼接为 ${attr}@nocalhost.dev,域名部分必须满足 LDH 规则,否则单个条目将无法同步,如果数据库已有此 email,则会直接建立映射关系,如果提供的 email 有重复,则可能出现数据错乱", + "usernameAttrTip": "如果不填或没有获取到,在导入时则会取 email 的域名部分作为用户名,其中超过 20 字符部分将被截断;如果已经存在此用户,且不填或没有获取到,则不会更新原有用户名。", + "exampleTip": "(填完至少基础 DN+Attr 实时获取一个)", + "deleteConfirm": "是否确认要删除配置" } } diff --git a/src/images/bg_left_select.svg b/src/images/bg_left_select.svg index beb4cbf..015263d 100644 --- a/src/images/bg_left_select.svg +++ b/src/images/bg_left_select.svg @@ -1,27 +1 @@ - - - bg_left_select - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/bg_left_unselect.svg b/src/images/bg_left_unselect.svg index f3b1e3e..0bcbabf 100644 --- a/src/images/bg_left_unselect.svg +++ b/src/images/bg_left_unselect.svg @@ -1,27 +1 @@ - - - bg_left_unselect - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/bg_right_select.svg b/src/images/bg_right_select.svg index 677d11e..5b37e21 100644 --- a/src/images/bg_right_select.svg +++ b/src/images/bg_right_select.svg @@ -1,27 +1 @@ - - - bg_right_select - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/bg_right_unselect.svg b/src/images/bg_right_unselect.svg index cfb1625..6602696 100644 --- a/src/images/bg_right_unselect.svg +++ b/src/images/bg_right_unselect.svg @@ -1,27 +1 @@ - - - bg_right_unselect - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon/icon_arrow_right.svg b/src/images/icon/icon_arrow_right.svg index cf241e5..807d95f 100644 --- a/src/images/icon/icon_arrow_right.svg +++ b/src/images/icon/icon_arrow_right.svg @@ -1,15 +1 @@ - - - arrow_right - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon/icon_ldap.svg b/src/images/icon/icon_ldap.svg index debaa75..0435511 100644 --- a/src/images/icon/icon_ldap.svg +++ b/src/images/icon/icon_ldap.svg @@ -1,16 +1 @@ - - - icon_ldap - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon/icon_set_elected.svg b/src/images/icon/icon_set_elected.svg index fd0c52d..adb545e 100644 --- a/src/images/icon/icon_set_elected.svg +++ b/src/images/icon/icon_set_elected.svg @@ -1,12 +1 @@ - - - icon_set_elected - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon/icon_set_normal.svg b/src/images/icon/icon_set_normal.svg index 4999ee3..29ca6d8 100644 --- a/src/images/icon/icon_set_normal.svg +++ b/src/images/icon/icon_set_normal.svg @@ -1,12 +1 @@ - - - icon_set_normal - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon_set_elected.svg b/src/images/icon_set_elected.svg index fd0c52d..adb545e 100644 --- a/src/images/icon_set_elected.svg +++ b/src/images/icon_set_elected.svg @@ -1,12 +1 @@ - - - icon_set_elected - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/images/icon_set_normal.svg b/src/images/icon_set_normal.svg index 4999ee3..29ca6d8 100644 --- a/src/images/icon_set_normal.svg +++ b/src/images/icon_set_normal.svg @@ -1,12 +1 @@ - - - icon_set_normal - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/pages/Setting/components/ConfigService.tsx b/src/pages/Setting/components/ConfigService.tsx index d76f9eb..94041e5 100644 --- a/src/pages/Setting/components/ConfigService.tsx +++ b/src/pages/Setting/components/ConfigService.tsx @@ -8,23 +8,30 @@ import LabelWithHelp from '../../../components/LabelWithHelp'; import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; import { ReactComponent as IconArrowRight } from '../../../images/icon/icon_arrow_right.svg'; +import HTTP from '../../../api/fetch'; + interface IProp { configData: ConfigInfo | null; onClose: () => void; + handleSyncData: () => void; + closeAndSync: () => void; } -const ConfigService = ({ configData, onClose }: IProp) => { +const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IProp) => { const { t } = useTranslation(); - const [currentStep, setCurrentStep] = useState(configData ? 2 : 1); + const [currentStep, setCurrentStep] = useState(configData?.enable ? 2 : 1); const [folded, setFolded] = useState(true); const [form] = Form.useForm(); + const [isLoading, setIsLoading] = useState(false); + // eslint-disable-next-line no-undef + const [step1Info, setStep1Info] = useState | null>(configData); useEffect(() => { if (configData) { form.setFieldsValue({ server: configData?.server, bind_dn: configData?.bind_dn, - password: '******', + password: configData?.password, security: configData?.tls ? 'tls' : configData?.md5 ? 'md5' : '', base_dn: configData?.base_dn, filter: configData?.filter, @@ -36,8 +43,80 @@ const ConfigService = ({ configData, onClose }: IProp) => { } }, []); - const handleNextStep = () => { - setCurrentStep(2); + const handleNextStep = async () => { + const { bind_dn, password, server, security } = form.getFieldsValue(); + setIsLoading(true); + setStep1Info({ + ...configData, + bind_dn, + password, + server, + tls: security === 'tls' ? 1 : 0, + md5: security === 'md5' ? 1 : 0, + }); + const response = await HTTP.put('ldap/bind', { + bind_dn, + password, + server, + }); + setIsLoading(false); + if (response.code === 0) { + setCurrentStep(2); + } + }; + + const validateSearch = async () => { + const { + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + } = form.getFieldsValue(); + const resp = await HTTP.put('ldap/search', { + ...step1Info, + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + }); + return resp; + }; + + const saveConfig = async () => { + const { + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + } = form.getFieldsValue(); + return await HTTP.put('ldap/config/set', { + ...step1Info, + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + }); + }; + + const handleSubmit = async () => { + setIsLoading(true); + const validate = await validateSearch(); + if (validate.code === 0) { + const saveInfo = await saveConfig(); + if (saveInfo.code === 0) { + await handleSyncData(); + setIsLoading(false); + closeAndSync(); + } + } }; return ( @@ -108,27 +187,47 @@ const ConfigService = ({ configData, onClose }: IProp) => { <> + } name="email_attr" required={true} > - + + } + name="user_name_attr" + >
{t('settings.memberRule')}
- + - +
-
Example: (填完至少基础 DN+Attr 实时获取一个)
+
{`Example: ${t('settings.exampleTip')}`}
DN: - -
Email: - -
Username: - -
@@ -152,17 +251,19 @@ const ConfigService = ({ configData, onClose }: IProp) => {
-
Example: (填完至少基础 DN+Attr 实时获取一个)
+
{`Example: ${t('settings.exampleTip')}`}
DN: - -
Email: - -
Username: - -
@@ -179,15 +280,19 @@ const ConfigService = ({ configData, onClose }: IProp) => { {t('common.bt.cancel')} {currentStep === 1 && ( - )} {currentStep === 2 && ( <> - diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx index a3abed2..7c19e2d 100644 --- a/src/pages/Setting/components/ThirdAccount.tsx +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -1,18 +1,65 @@ -import React from 'react'; -import { ThirdAccountWrap } from '../styled-component'; -import { Button } from 'antd'; +import React, { useState } from 'react'; +import { ThirdAccountWrap, PopupWrap } from '../styled-component'; +import { Button, Popover } from 'antd'; import { useTranslation } from 'react-i18next'; import { ReactComponent as IconLdap } from '../../../images/icon/icon_ldap.svg'; +import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; import Icon from '@ant-design/icons'; +import DeleteModal from '../../../components/DeleteModal'; interface ThirdAccountProp { - status: string; + status: 'unallocated' | 'configured'; showConfig: () => void; + handleSyncData: () => void; + handleDeleteConfig: () => void; } -const ThirdAccount = ({ status, showConfig }: ThirdAccountProp) => { +const ThirdAccount = ({ + status, + showConfig, + handleSyncData, + handleDeleteConfig, +}: ThirdAccountProp) => { const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [showDelete, setShowDelete] = useState(false); + + const handleEditConfig = () => { + setVisible(false); + showConfig(); + }; + + const handleDelete = () => { + setVisible(false); + handleDeleteConfig(); + }; + + const handleClick = async () => { + setIsLoading(true); + await handleSyncData(); + setIsLoading(false); + }; + + const onClickDelete = () => { + setVisible(false); + setShowDelete(true); + }; + + const PopupContent = () => { + return ( + +
  • + {t('settings.modifyConfig')} +
  • +
  • + {t('settings.deleteConfig')} +
  • +
    + ); + }; + return (
    @@ -31,10 +78,39 @@ const ThirdAccount = ({ status, showConfig }: ThirdAccountProp) => {
    - + {status === 'unallocated' && ( + + )} + {status === 'configured' && ( + <> + + } + visible={visible} + onVisibleChange={(curr: boolean) => setVisible(curr)} + trigger="click" + > + + + + )}
    + setShowDelete(false)} + onConfirm={handleDelete} + /> ); }; diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx index 2baaa13..e80cf7c 100644 --- a/src/pages/Setting/index.tsx +++ b/src/pages/Setting/index.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { SettingWrap } from './styled-component'; import ThirdAccount from './components/ThirdAccount'; import ConfigService from './components/ConfigService'; -import { Modal } from 'antd'; +import { message, Modal } from 'antd'; import HTTP from '../../api/fetch'; import { ConfigInfo } from '../../types'; @@ -21,17 +21,37 @@ const Settings = () => { setShowModal(true); }; + const handleSyncData = async () => { + const response = await HTTP.post('ldap/trigger', {}); + if (response.code === 0) { + message.success(t('common.message.success')); + } + }; + + // delete config + const handleDeleteConfig = async () => { + const resp = await HTTP.put('ldap/config/disable', configData); + if (resp.code === 0) { + await getConfig(); + } + }; + const getConfig = async () => { - const response = await HTTP.get('/ldap/config'); + const response = await HTTP.get('ldap/config'); if (response.code === 0) { const { data } = response; if (data) { - setStatus('configured'); + setStatus(data?.enable ? 'configured' : 'unallocated'); setConfigData(data); } } }; + const closeAndSync = async () => { + setShowModal(false); + await getConfig(); + }; + useEffect(() => { getConfig(); }, []); @@ -59,7 +79,12 @@ const Settings = () => {
    {t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
    - +
    { visible={showModal} onCancel={() => setShowModal(false)} footer={null} + bodyStyle={{ paddingTop: 0 }} > - setShowModal(false)} /> + setShowModal(false)} + closeAndSync={closeAndSync} + handleSyncData={handleSyncData} + /> ); diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx index 39197c5..4d92fb3 100644 --- a/src/pages/Setting/styled-component.tsx +++ b/src/pages/Setting/styled-component.tsx @@ -110,6 +110,16 @@ export const ThirdAccountWrap = styled.div` } } } + + .btn-box { + display: flex; + align-items: center; + + .popup-btn { + display: flex; + align-items: center; + } + } `; export const ConfigServiceWrap = styled.div` @@ -166,6 +176,7 @@ export const ConfigServiceWrap = styled.div` width: 314px; position: absolute; padding-left: 40px; + padding-right: 0; left: 50%; background: url(${(props) => (props.step === 2 ? step2active : step2normal)}) no-repeat; @@ -270,9 +281,37 @@ export const ConfigServiceWrap = styled.div` margin-top: 24px; display: flex; justify-content: flex-end; + align-items: center; } .ant-form-item-control-input { box-shadow: none; } `; + +export const PopupWrap = styled.ul` + .list-item { + display: flex; + align-items: center; + justify-content: center; + width: 106px; + height: 32px; + cursor: pointer; + + &:hover { + background: #f3f6fa; + } + + font-family: PingFangSC-Regular; + font-size: 14px; + font-weight: normal; + + &.modify { + color: rgb(32, 45, 64); + } + + &.del { + color: #ff3f3f; + } + } +`; diff --git a/src/types/index.ts b/src/types/index.ts index e650c4c..93d49fe 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -46,11 +46,11 @@ export interface ConfigInfo { inserts: number; last_sync_err_msg: string; ldap_gen: number; - md5: string; + md5: number; password: string; server: string; sync_protection_ts: number; - tls: string; + tls: number; updates: number; user_name_attr: string; } From b46e70688dc52e4509f67679096698e210b23d8d Mon Sep 17 00:00:00 2001 From: yuanganping Date: Tue, 4 Jan 2022 14:09:43 +0800 Subject: [PATCH 05/10] feat: add validate Signed-off-by: yuanganping --- src/components/MainHeader/index.tsx | 2 +- src/i18n/en.json | 6 +- src/i18n/zh.json | 4 +- src/pages/Application/index.tsx | 20 +-- src/pages/Clusters/components/ListItem.tsx | 2 +- src/pages/Clusters/index.tsx | 11 +- src/pages/EnvList/index.tsx | 11 +- .../Setting/components/ConfigService.tsx | 149 +++++++++++++----- src/pages/Setting/components/ThirdAccount.tsx | 20 ++- src/pages/Setting/index.tsx | 2 + src/pages/Setting/styled-component.tsx | 8 + src/types/index.ts | 1 + 12 files changed, 155 insertions(+), 81 deletions(-) diff --git a/src/components/MainHeader/index.tsx b/src/components/MainHeader/index.tsx index 1053912..233a8de 100644 --- a/src/components/MainHeader/index.tsx +++ b/src/components/MainHeader/index.tsx @@ -465,7 +465,7 @@ function MainHeader() { current: upgradeInfo?.current_version, })} - + {t('common.message.link')}
    diff --git a/src/i18n/en.json b/src/i18n/en.json index de8b22a..c5cf255 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -529,7 +529,7 @@ }, "settings": { "settingCenter": "Configuration Center", - "thirdAccount": "Third party account configuration", + "thirdAccount": "Third party account config", "configService": "Configuration Services", "unallocated": "unallocated", "configured": "configured", @@ -555,6 +555,7 @@ "usernameAttrPh": "Please enter the attribute name of the user name, e.g. OpenLdap Fill in the uid ", "memberRule": "Membership Rules", "baseDn": "Basic DN", + "baseDnPh": "Search for users from the root directory, e.g. cn=users,dc=example.com,de= ", "selector": "Filters", "advanceSetting": "Advanced Configuration", "advanceSettingTip": "Users who meet this base DN and filter will be imported as an administrator user group.", @@ -566,6 +567,7 @@ "emailAttrTip": "If the email provided is not an email, Nocalhost will automatically splice it as ${attr}@nocalhost.dev, the domain part must meet the LDH rules, otherwise the single entry will not be synchronized, if the database already has this email, the mapping relationship will be established directly, if the email provided is duplicated, the data may be misplaced", "usernameAttrTip": "If it is not filled in or not obtained, the domain part of the email will be taken as the username when importing, and the part of the email that exceeds 20 characters will be truncated; if the user already exists and is not filled in or not obtained, the original username will not be updated.", "exampleTip": "(Fill out at least the base DN+Attr to get one in real time)", - "deleteConfirm": "Is it confirmed that you want to delete the configuration" + "deleteConfirm": "Is it confirmed that you want to delete the configuration", + "filterTip": "Default value is (objectClass=*) " } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 40c28b6..e8dbb29 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -556,6 +556,7 @@ "usernameAttrPh": "请输入用户名称的属性名,如 OpenLdap 填写 uid ", "memberRule": "成员规则", "baseDn": "基础 DN", + "baseDnPh": "从根目录搜索用户,如 cn=users,dc=example.com,de= ", "selector": "筛选器", "advanceSetting": "高级配置", "advanceSettingTip": "满足此 基础 DN 和 筛选器 的用户将会被导入为 管理员用户组。", @@ -567,6 +568,7 @@ "emailAttrTip": "如果提供的不是一个 email,则 Nocalhost 将自动为其拼接为 ${attr}@nocalhost.dev,域名部分必须满足 LDH 规则,否则单个条目将无法同步,如果数据库已有此 email,则会直接建立映射关系,如果提供的 email 有重复,则可能出现数据错乱", "usernameAttrTip": "如果不填或没有获取到,在导入时则会取 email 的域名部分作为用户名,其中超过 20 字符部分将被截断;如果已经存在此用户,且不填或没有获取到,则不会更新原有用户名。", "exampleTip": "(填完至少基础 DN+Attr 实时获取一个)", - "deleteConfirm": "是否确认要删除配置" + "deleteConfirm": "是否确认要删除配置", + "filterTip": "缺省值为(objectClass=*) " } } diff --git a/src/pages/Application/index.tsx b/src/pages/Application/index.tsx index b95fef6..4e0c581 100644 --- a/src/pages/Application/index.tsx +++ b/src/pages/Application/index.tsx @@ -23,7 +23,6 @@ import DeleteModal from '../../components/DeleteModal'; import LabelSelect from '../../components/LabelSelect'; // import { applictionOptions } from './const'; import { useHistory } from 'react-router-dom'; -import { UserType } from '../User/const'; import CommonIcon from '../../components/CommonIcon'; import { ReactComponent as IconNormalEdit } from '../../images/icon/icon_btn_normal_edit.svg'; import { ReactComponent as IconSelectedEdit } from '../../images/icon/icon_btn_elected_edit.svg'; @@ -42,7 +41,6 @@ import { ReactComponent as IconAdd } from '../../images/icon/icon_add.svg'; function Application() { const [data, setData] = useState([]); const [copyData, setCopyData] = useState([]); - const [userList, setUserList] = useState([]); const [tableLoading, setTableLoading] = useState(false); const [openDialog, setOpenDialog] = useState(false); const [filterValue, setFilterValue] = useState({ name: '', type: 'all' }); @@ -60,12 +58,7 @@ function Application() { const { user } = useContext(UserContext); const history = useHistory(); const { t } = useTranslation(); - const getUser = async () => { - const result = await HTTP.get('users'); - if (result.code === 0) { - setUserList(result.data || []); - } - }; + const getApplication = async () => { setTableLoading(true); const result = await HTTP.get('application', { @@ -81,7 +74,6 @@ function Application() { }; useEffect(() => { getApplication(); - getUser(); }, []); const showTotal = (total: number) => { return `共${total}条`; @@ -230,16 +222,8 @@ function Application() { }, { title: t('resources.application.fields.user'), + dataIndex: 'user_name', key: '5', - // eslint-disable-next-line react/display-name - render: (...args: any) => { - const record = args[1]; - const user_id = record?.user_id; - const item: UserType = userList.find((el: UserType) => el.id === user_id) || { - name: '', - }; - return {item?.name}; - }, }, { title: t('common.operation'), diff --git a/src/pages/Clusters/components/ListItem.tsx b/src/pages/Clusters/components/ListItem.tsx index 465cf31..ee2e1ea 100644 --- a/src/pages/Clusters/components/ListItem.tsx +++ b/src/pages/Clusters/components/ListItem.tsx @@ -496,7 +496,7 @@ const ListItem: FC = ({ data, onSubmit }: IProps) => { component={IconProfile} style={{ fontSize: 24, marginRight: 6 }} /> - {data.userName} + {data.user_name} diff --git a/src/pages/Clusters/index.tsx b/src/pages/Clusters/index.tsx index daf51eb..b7786a2 100644 --- a/src/pages/Clusters/index.tsx +++ b/src/pages/Clusters/index.tsx @@ -6,7 +6,6 @@ import { ContentTitle, ClusterCount, FlexContainer, LoadingBox } from './style-c import { Button, Spin } from 'antd'; import i18n from '../../i18n/i18n'; import AddCluster from '../../components/AddCluster'; -import { queryAllUser } from '../../services'; import { useTranslation } from 'react-i18next'; import NotData from '../../components/NotData'; import Icon from '@ant-design/icons'; @@ -23,17 +22,11 @@ const Clusters: FC<{}> = () => { async function queryClusters() { setClusterLoading(true); - const nameMap = await queryAllUser(); + const response = await HTTP.get('cluster'); setClusterLoading(false); if (response.code === 0) { - const tmpList = response.data.map((item: any) => { - return { - ...item, - userName: nameMap.get(item.user_id), - }; - }); - setClusterList(tmpList); + setClusterList(response.data); } } diff --git a/src/pages/EnvList/index.tsx b/src/pages/EnvList/index.tsx index 62cfad2..713c562 100644 --- a/src/pages/EnvList/index.tsx +++ b/src/pages/EnvList/index.tsx @@ -248,12 +248,12 @@ const EnvList = () => { return record?.cooper_user?.length > 0 || record?.viewer_user?.length > 0 ? ( }> - {record.user_name} + {record?.owner?.name} ) : ( - {record.user_name} + {record?.owner?.name} ); }, }, @@ -604,13 +604,6 @@ const EnvList = () => { /> - {/* - - */} {!id && (
    )} diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx index 7c19e2d..50343a1 100644 --- a/src/pages/Setting/components/ThirdAccount.tsx +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -5,13 +5,15 @@ import { useTranslation } from 'react-i18next'; import { ReactComponent as IconLdap } from '../../../images/icon/icon_ldap.svg'; import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; +import { ReactComponent as IconExplain } from '../../../images/icon/icon_label_explain.svg'; import Icon from '@ant-design/icons'; import DeleteModal from '../../../components/DeleteModal'; +import CommonIcon from '../../../components/CommonIcon'; interface ThirdAccountProp { status: 'unallocated' | 'configured'; showConfig: () => void; - handleSyncData: () => void; + handleSyncData: () => any; handleDeleteConfig: () => void; } @@ -25,6 +27,8 @@ const ThirdAccount = ({ const [visible, setVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); const [showDelete, setShowDelete] = useState(false); + const [showSyncTip, setShowSyncTip] = useState(false); + const [syncTip, setSyncTip] = useState(''); const handleEditConfig = () => { setVisible(false); @@ -38,7 +42,12 @@ const ThirdAccount = ({ const handleClick = async () => { setIsLoading(true); - await handleSyncData(); + const resp = await handleSyncData(); + if (resp.code !== 0) { + setSyncTip(resp.message); + setShowSyncTip(true); + } + setIsLoading(false); }; @@ -78,6 +87,13 @@ const ThirdAccount = ({
    + {showSyncTip && ( + + )} {status === 'unallocated' && (
    diff --git a/src/services/index.ts b/src/services/index.ts index 8879a70..715b240 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -12,6 +12,22 @@ export const queryAllUser = async () => { return nameMap; }; +export const queryUserList = async () => { + const response = await HTTP.get('users'); + let list = []; + if (response.code === 0) { + list = response?.data?.map((item: any) => { + return { + label: item.name, + value: item.id, + text: item.name, + }; + }); + } + + return list; +}; + export const queryAllCluster = async () => { const response = await HTTP.get('dev_space/cluster', null, { is_v2: true, From eef3d295bd788987cb78b67923e2a9b36b03c8ce Mon Sep 17 00:00:00 2001 From: yuanganping Date: Mon, 17 Jan 2022 10:35:13 +0800 Subject: [PATCH 07/10] change status Signed-off-by: yuanganping --- src/index.css | 4 +++ .../Setting/components/ConfigService.tsx | 30 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/index.css b/src/index.css index de491a8..dae657a 100644 --- a/src/index.css +++ b/src/index.css @@ -331,3 +331,7 @@ input:-webkit-autofill:active { .ant-tooltip-arrow-content { background-color: #1a2433; } + +.ant-form-item-required { + font-weight: normal; +} diff --git a/src/pages/Setting/components/ConfigService.tsx b/src/pages/Setting/components/ConfigService.tsx index bcd10c8..9cf01b2 100644 --- a/src/pages/Setting/components/ConfigService.tsx +++ b/src/pages/Setting/components/ConfigService.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { ConfigServiceWrap } from '../styled-component'; import { useTranslation } from 'react-i18next'; -import { Form, Input, Radio, Button } from 'antd'; +import { Form, Input, Button, Checkbox } from 'antd'; import { ConfigInfo } from '../../../types'; import LabelWithHelp from '../../../components/LabelWithHelp'; import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; @@ -36,11 +36,18 @@ const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IP useEffect(() => { if (configData) { + const security = []; + if (configData.tls) { + security.push('tls'); + } + if (configData?.md5) { + security.push('md5'); + } form.setFieldsValue({ server: configData?.server, bind_dn: configData?.bind_dn, password: configData?.password, - security: configData?.tls ? 'tls' : configData?.md5 ? 'md5' : '', + security, base_dn: configData?.base_dn, filter: configData?.filter, email_attr: configData?.email_attr, @@ -62,8 +69,8 @@ const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IP bind_dn, password, server, - tls: security === 'tls' ? 1 : 0, - md5: security === 'md5' ? 1 : 0, + tls: security.indexOf('tls') > -1 ? 1 : 0, + md5: security.indexOf('md5') ? 1 : 0, }); const response = await HTTP.put('ldap/bind', { bind_dn, @@ -163,10 +170,6 @@ const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IP }); }; - const handleFieldChange = () => { - // console - }; - return (
    {t('settings.configDesc')}
    @@ -194,7 +197,6 @@ const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IP }} labelAlign="left" form={form} - onFieldsChange={handleFieldChange} > {currentStep === 1 && ( <> @@ -225,12 +227,12 @@ const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IP - - Enable TLS - Enable Digest MD5 Bind - + + Enable TLS + Enable Digest MD5 Bind + )} From 24673c419c56b907af52097cbcf9ba32c94e4200 Mon Sep 17 00:00:00 2001 From: yuanganping Date: Mon, 17 Jan 2022 15:45:04 +0800 Subject: [PATCH 08/10] change translate Signed-off-by: yuanganping --- src/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index c5cf255..9d28248 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -554,7 +554,7 @@ "usernameAttr": "Username attr", "usernameAttrPh": "Please enter the attribute name of the user name, e.g. OpenLdap Fill in the uid ", "memberRule": "Membership Rules", - "baseDn": "Basic DN", + "baseDn": "Base DN", "baseDnPh": "Search for users from the root directory, e.g. cn=users,dc=example.com,de= ", "selector": "Filters", "advanceSetting": "Advanced Configuration", From c759fcd8b6fa4762f546f78855695b39ab119ddc Mon Sep 17 00:00:00 2001 From: yuanganping Date: Wed, 19 Jan 2022 13:42:46 +0800 Subject: [PATCH 09/10] style: change label padding Signed-off-by: yuanganping --- src/pages/Setting/styled-component.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx index 1bb7ece..e6247af 100644 --- a/src/pages/Setting/styled-component.tsx +++ b/src/pages/Setting/styled-component.tsx @@ -250,6 +250,7 @@ export const ConfigServiceWrap = styled.div` border: 1px solid rgb(218, 225, 232); .rule-label { + padding: 0 4px; position: absolute; background: #f9fbfd; top: 0; From 75434ae6bece0aea2417a5edd0bb37164109ab8d Mon Sep 17 00:00:00 2001 From: yuanganping Date: Wed, 26 Jan 2022 17:43:33 +0800 Subject: [PATCH 10/10] feat: change donwlaod kubeconfig type Signed-off-by: yuanganping --- src/pages/EnvList/components/KubeConfig.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/EnvList/components/KubeConfig.tsx b/src/pages/EnvList/components/KubeConfig.tsx index 9478912..fb6a7ea 100644 --- a/src/pages/EnvList/components/KubeConfig.tsx +++ b/src/pages/EnvList/components/KubeConfig.tsx @@ -69,7 +69,9 @@ const KubeConfig = (props: PropParam) => { const handleDownload = () => { // download - const blob = new Blob([kubeConfig]); + const blob = new Blob([kubeConfig], { + type: 'application/octet-stream', + }); const aLink = document.createElement('a'); aLink.style.display = 'none'; aLink.setAttribute('download', 'config');