From e60020c9d87dafa025d175d7a47d246f37b92203 Mon Sep 17 00:00:00 2001 From: dark Date: Sun, 21 Jul 2024 18:18:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=95=B4=E8=AF=81=E4=B9=A6=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.css | 4 ++ src/components/copy/index.tsx | 53 ++++++++++++++ src/locales/lang/en-US.ts | 2 + src/locales/lang/zh-CN.ts | 2 + src/pages/websites/cert/apply.tsx | 141 ++++++++++++++++++++++++++++++++------ src/service/websites.ts | 57 --------------- src/store/websites/cert.ts | 44 +++++++++--- 7 files changed, 217 insertions(+), 86 deletions(-) create mode 100644 src/components/copy/index.tsx diff --git a/src/App.css b/src/App.css index 06896a5..f457a94 100644 --- a/src/App.css +++ b/src/App.css @@ -50,6 +50,10 @@ color: green; } +.color-green1{ + color: rgb(18 185 128 / 1 ); +} + .color-red { color: #F56C6C; } diff --git a/src/components/copy/index.tsx b/src/components/copy/index.tsx new file mode 100644 index 0000000..c35e1fe --- /dev/null +++ b/src/components/copy/index.tsx @@ -0,0 +1,53 @@ +import { Tooltip } from 'antd' +import { CopyConfig } from 'antd/es/typography/Base' +import { useCopyToClipboard, } from 'react-use' +import { useState } from 'react' +import { t } from 'i18next' +import { CheckOutlined } from '@ant-design/icons' + +export interface ICopyProps extends CopyConfig { + +} + +const Copy = ({ tooltips = true, text, icon = , onCopy }: ICopyProps) => { + + const [ , copyToClipboard ] = useCopyToClipboard() + const [ showIcon, setShow ] = useState(false) + + const getText = (): any => { + if (typeof text === 'function') { + return text() + } + return text + } + + const render = () => { + if (tooltips) { + return + {getText()} + + } + return getText() + } + + return ( + <> + { + onCopy?.() + setShow(true) + copyToClipboard(getText()) + setTimeout(() => { + setShow(false) + }, 2000) + }}> + {render()} + + {showIcon && icon} + + + + ) +} + +export default Copy \ No newline at end of file diff --git a/src/locales/lang/en-US.ts b/src/locales/lang/en-US.ts index de8dacf..364a702 100644 --- a/src/locales/lang/en-US.ts +++ b/src/locales/lang/en-US.ts @@ -63,6 +63,8 @@ export default { reset: 'Reset', clear: 'Clear', close: 'Close', + copy: 'copy', + clickCopy: 'click copy', }, message: { infoTitle: 'Hint', diff --git a/src/locales/lang/zh-CN.ts b/src/locales/lang/zh-CN.ts index 4d0a87c..1623ddd 100644 --- a/src/locales/lang/zh-CN.ts +++ b/src/locales/lang/zh-CN.ts @@ -75,6 +75,8 @@ export default { reset: '重置', clear: '清空', close: '关闭', + copy: '复制', + clickCopy: '点击复制', }, message: { infoTitle: '提示', diff --git a/src/pages/websites/cert/apply.tsx b/src/pages/websites/cert/apply.tsx index deb6ad1..f5045b2 100644 --- a/src/pages/websites/cert/apply.tsx +++ b/src/pages/websites/cert/apply.tsx @@ -1,7 +1,12 @@ import { t } from '@/i18n.ts' -import { useAtomValue } from 'jotai' -import { algorithmTypes, bandTypes, dnsConfigAtom, saveOrUpdateCertAtom, StatusText } from '@/store/websites/cert.ts' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useAtom, useAtomValue, useSetAtom } from 'jotai' +import { + algorithmTypes, + dnsConfigAtom, + dnsVerifyAtom, dnsVerifyOKAtom, + saveOrUpdateCertAtom, +} from '@/store/websites/cert.ts' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Button, Flex, @@ -9,7 +14,7 @@ import { Input, Select, Space, - Table, Typography, + Table, Tooltip, } from 'antd' import google from '@/pages/websites/cert/assets/google.png' import zerossl from '@/pages/websites/cert/assets/zerossl.png' @@ -17,6 +22,9 @@ import lets_encrypt from '@/pages/websites/cert/assets/lets_encrypt.png' import { useStyle } from './style' import ListPageLayout from '@/layout/ListPageLayout.tsx' import { ColumnsType } from 'antd/es/table' +import { atomWithStorage } from 'jotai/utils' +import Copy from '@/components/copy' +import { InfoCircleOutlined, LoadingOutlined } from '@ant-design/icons' const i18nPrefix = 'cert.apply' @@ -68,46 +76,117 @@ const StatusTable = (props: { value: string }) => { const { data, isFetching } = useAtomValue(useMemo(() => dnsConfigAtom(props.value), [ props.value ])) + const { + data: dnsVerifyStatus, + isFetching: isVerifyFetching, + refetch + } = useAtomValue(useMemo(() => dnsVerifyAtom(props.value, isFetching), [ props.value, isFetching ])) + + const setDnsVerifyOK = useSetAtom(dnsVerifyOKAtom) + + const timerRef = useRef() + const columns = useMemo(() => { return [ { - title: t(`${i18nPrefix}.status.columns.status`, '状态'), - tooltip: t(`${i18nPrefix}.status.columns.statusTip`, '正确配置DNS解析后,域名验证会自动通过'), + title: <>{t(`${i18nPrefix}.status.columns.status`, '状态')} , dataIndex: 'status', + width: 100, + render: (_, record) => { + if (isFetching) { + return {t(`${i18nPrefix}.actions.dnsVerifyStatus.0`, '等待')} + } + + if (isVerifyFetching) { + //0,等待 1,域名OK,2,域名分析错误,3:检测中 4:检测成功,匹配失败 5:检测失败,9:检测成功 + return {t(`${i18nPrefix}.actions.dnsVerifyStatus.3`, '检测中')} + } + const dns = record.dns_name + const info = (dnsVerifyStatus as any)?.find((item) => item.dns_name === dns) as any + if (info) { + return {t(`${i18nPrefix}.actions.dnsVerifyStatus.${info.status}`, `${info?.status_txt}`)} + } + return {t(`${i18nPrefix}.actions.dnsVerifyStatus.0`, '等待')} + }, }, { //服务商 title: t(`${i18nPrefix}.status.columns.name_servers`, '服务商'), dataIndex: 'name_servers', + width: 150, + render(text) { + if (text) { + return {text} + } + return 未知 + } }, { //域名 title: t(`${i18nPrefix}.status.columns.domain`, '域名'), - dataIndex: 'domain', + dataIndex: 'dns_name', + width: 200, }, { //主机记录 title: t(`${i18nPrefix}.status.columns.record`, '主机记录'), - dataIndex: 'record', + dataIndex: 'host', + width: 200, + render: (text) => { + return + } }, { //记录类型 title: t(`${i18nPrefix}.status.columns.record_type`, '记录类型'), - dataIndex: 'record_type', + dataIndex: 'type', + width: 100, + render: (text) => { + return {text} + } }, { //记录值 title: t(`${i18nPrefix}.status.columns.record_value`, '记录值'), dataIndex: 'record_value', - render:(text)=>{ - return {text} + width: 200, + render: (text) => { + return } } ] as ColumnsType - }, []) + }, [ isFetching, isVerifyFetching, dnsVerifyStatus ]) + useEffect(() => { + + if ((dnsVerifyStatus as any)?.every((item) => item.status === 9)) { + setDnsVerifyOK(true) + return + } + + timerRef.current = window.setInterval(() => { + if (isVerifyFetching) { + return + } + //dnsVerifyStatus 如果所有status 为 9 则说明域名验证通过 + if ((dnsVerifyStatus as any)?.every((item) => item.status === 9)) { + setDnsVerifyOK(true) + window.clearInterval(timerRef.current) + } else { + refetch() + } + }, 2000) + + return () => { + window.clearInterval(timerRef.current) + } + + }, [ dnsVerifyStatus, isVerifyFetching ]) return <>
@@ -124,11 +203,24 @@ const StatusTable = (props: { value: string }) => { } -const Apply = (props: any) => { - const { styles, cx } = useStyle() +const domainsAtom = atomWithStorage('domains', '') + + +const Apply = ( ) => { + const { styles } = useStyle() const [ form ] = Form.useForm() - const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCertAtom) - const [ domains, setDomains ] = useState('') + const { mutate: saveOrUpdate, isPending: isSubmitting } = useAtomValue(saveOrUpdateCertAtom) + const [ domains, setDomains ] = useAtom(domainsAtom) + const dnsVerifyOK = useAtomValue(dnsVerifyOKAtom) + + useEffect(() => { + + if (domains) { + form.setFieldsValue({ + domains + }) + } + }, [ domains ]) return ( { onValuesChange={(values) => { // console.log('onValuesChange', values) if (values.domains) { - setDomains(values.domains) + // setDomains(values.domains) } }} onFinish={async (values) => { - console.log(values) - saveOrUpdate(values) + if (dnsVerifyOK) { + saveOrUpdate(values) + } }} > { placeholder={`请输入域名,每行一个,支持泛解析域名;如: *.google.com *.a.baidu.com - hello.alibaba.com`}/> + hello.alibaba.com`} + onBlur={(e) => { + setDomains(e.target.value) + }} + /> { - + diff --git a/src/service/websites.ts b/src/service/websites.ts index 703da75..c57bed8 100644 --- a/src/service/websites.ts +++ b/src/service/websites.ts @@ -8,63 +8,6 @@ import { IWebsiteDnsAccount } from '@/types/website/dns_account' const websitesServ = { cert: { ...createCURD('/website/cert'), - list: async (params?: any) => { - console.log(params) - return Promise.resolve({ - 'code': 0, - 'msg': 'success', - 'data': { - 'total': 3, - 'rows': [ - { - 'id': 10266, - 'brand': 'ZeroSSL', - 'domains': '*.aa.com,*.abc.com,aa.com,abc.com', - 'type': 4, - 'level': 1, - 'status': 3, - 'createTime': '2024-06-29 14:36:02', - 'remark': '', - 'algorithm': 'ECC', - 'fromType': 2 - }, - { - 'id': 10265, - 'brand': 'ZeroSSL', - 'domains': '*.aa.com,*.abc.com,aa.com,abc.com', - 'type': 4, - 'level': 1, - 'status': 3, - 'createTime': '2024-06-29 14:35:58', - 'remark': '', - 'algorithm': 'ECC', - 'fromType': 2 - }, - { - 'id': 10261, - 'brand': 'Google', - 'domains': '*.3456.world,3456.world', - 'type': 3, - 'level': 1, - 'status': 1, - 'createTime': '2024-06-29 14:17:46', - 'notAfter': '2024-09-27 13:18:31', - 'notBefore': '2024-06-29 13:18:32', - 'version': 3, - 'sigAlgName': 'SHA256withRSA', - 'remark': '3456.world', - 'name': '*.3456.world', - 'lifeTime': 90, - 'remainingTime': 82, - 'algorithm': 'RSA', - 'bit': 2048, - 'fromType': 2 - } - ] - }, - }) - // return request.post('/website/cert/list', params) - }, //dns_config dnsConfig: async (params: any) => { return request.post('/website/cert/dns_config', params) diff --git a/src/store/websites/cert.ts b/src/store/websites/cert.ts index 6cf7b55..9e1ff01 100644 --- a/src/store/websites/cert.ts +++ b/src/store/websites/cert.ts @@ -72,7 +72,7 @@ export const saveOrUpdateCertAtom = atomWithMutation(( mutationKey: [ 'updateCert' ], mutationFn: async (data) => { //data.status = data.status ? '1' : '0' - if ( data.id) { + if (data.id) { return await websitesServ.cert.update(data) } return await websitesServ.cert.add(data) @@ -107,26 +107,54 @@ export const deleteCertAtom = atomWithMutation((get) => { }) //dnsConfig -export const dnsConfigAtom = (domains: string[] | string) => atomWithQuery(() => { - if (typeof domains === 'string') { - domains = domains.split('\n').filter(Boolean) - } +export const dnsConfigAtom = (domains: string) => atomWithQuery(() => { return { - enabled: domains.length > 0, + enabled: domains.length > 0 && domains.includes('.'), queryKey: [ 'dnsConfig', domains ], queryFn: async ({ queryKey: [ , domains ] }) => { - if ((domains as string[]).length === 0){ + if ((domains as string).length === 0) { return Promise.reject({ data: [] }) } - return await websitesServ.cert.dnsConfig({ dns_full_list: (domains as string[]).filter(Boolean), parse: true }) + return await websitesServ.cert.dnsConfig({ + dns_full_list: domains, + parse: (domains as string)?.includes('*') + }) }, select: res => { return res.data } } }) + +export const dnsVerifyOKAtom = atom(false) + +//query dnsVerify +export const dnsVerifyAtom = (domains: string, block: boolean) => atomWithQuery(() => { + + return { + enabled: !block && domains.length > 0 && domains.includes('.'), + queryKey: [ 'dnsVerify', domains ], + queryFn: async ({ queryKey: [ , domains ] }) => { + + if ((domains as string).length === 0) { + return Promise.reject({ + data: [] + }) + } + + return await websitesServ.cert.dnsVerify({ + dns_list: domains, + }) + }, + select: res => { + return res.data?.dns_list + } + } +}) + +