From 9e047fc21845901499f4dca09b4d1639d801766a Mon Sep 17 00:00:00 2001 From: dark Date: Sun, 7 Jul 2024 19:33:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=A0=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E7=94=B3=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/action/Action.tsx | 6 +- src/components/modal-pro/index.tsx | 5 +- src/components/table/style.ts | 5 +- src/pages/websites/cert/assets/google.png | Bin 0 -> 8569 bytes src/pages/websites/cert/assets/lets_encrypt.png | Bin 0 -> 3466 bytes src/pages/websites/cert/assets/zerossl.png | Bin 0 -> 2828 bytes src/pages/websites/cert/index.tsx | 394 ++++++++++++++++++++++++ src/pages/websites/cert/style.ts | 26 ++ src/service/websites.ts | 68 +++- src/store/websites/cert.ts | 107 +++++++ src/types/website/cert.d.ts | 20 ++ 12 files changed, 625 insertions(+), 10 deletions(-) create mode 100644 src/pages/websites/cert/assets/google.png create mode 100644 src/pages/websites/cert/assets/lets_encrypt.png create mode 100644 src/pages/websites/cert/assets/zerossl.png create mode 100644 src/pages/websites/cert/index.tsx create mode 100644 src/pages/websites/cert/style.ts create mode 100644 src/store/websites/cert.ts create mode 100644 src/types/website/cert.d.ts diff --git a/src/App.css b/src/App.css index cffcefd..06896a5 100644 --- a/src/App.css +++ b/src/App.css @@ -50,6 +50,10 @@ color: green; } +.color-red { + color: #F56C6C; +} + .color-yellow { color: rgb(250 145 0) } diff --git a/src/components/action/Action.tsx b/src/components/action/Action.tsx index e2e0ea9..c48319a 100644 --- a/src/components/action/Action.tsx +++ b/src/components/action/Action.tsx @@ -10,12 +10,12 @@ const Action = ({ title, as, children, ...props }: ActionProps) => { const { styles } = useStyle() const isLink = as === 'a' || props.type === 'link' - + const Comp = isLink ? 'a' : Button return ( - + className={as === 'a' ? styles.actionA : ''}>{title ?? children} ) } diff --git a/src/components/modal-pro/index.tsx b/src/components/modal-pro/index.tsx index b6af677..b7f990c 100644 --- a/src/components/modal-pro/index.tsx +++ b/src/components/modal-pro/index.tsx @@ -7,10 +7,11 @@ export interface AlterProps extends ModalFuncProps { onLoad?: () => void alterType: keyof HookAPI | 'dialog' children: JSX.Element | React.ReactNode + disabled?: boolean } -const ModalPro = ({ onLoad, alterType, children, ...props }: AlterProps) => { +const ModalPro = ({ onLoad, alterType, children, disabled, ...props }: AlterProps) => { const [ modal, modalHolder ] = Modal.useModal() const [ dialogRef, dialog, ] = useDialog(props) @@ -19,6 +20,8 @@ const ModalPro = ({ onLoad, alterType, children, ...props }: AlterProps) => { return ( <> { + if (disabled) return + if (onLoad) { onLoad() } diff --git a/src/components/table/style.ts b/src/components/table/style.ts index 4804c32..7ba8688 100644 --- a/src/components/table/style.ts +++ b/src/components/table/style.ts @@ -50,8 +50,7 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) const pagination = { - borderTop: '1px solid #ebeef5', - height: '51px', + margin: '0', alignContent: 'center', display: 'flex', @@ -82,6 +81,8 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) justify-content: space-between; align-items: center; overflow: hidden; + border-top: 1px solid #ebeef5; + height: 51px; .ant-pro-table-alert{ max-width: 50%; diff --git a/src/pages/websites/cert/assets/google.png b/src/pages/websites/cert/assets/google.png new file mode 100644 index 0000000000000000000000000000000000000000..f553f3e4bb7cb950f9477d94ed1fec69a75d2ff2 GIT binary patch literal 8569 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 literal 0 HcmV?d00001 diff --git a/src/pages/websites/cert/assets/zerossl.png b/src/pages/websites/cert/assets/zerossl.png new file mode 100644 index 0000000000000000000000000000000000000000..0b54a4a062270ab3d5532f3a552896ee3f02fd14 GIT binary patch literal 2828 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 I8VDxp-?AhDJOBUy literal 0 HcmV?d00001 diff --git a/src/pages/websites/cert/index.tsx b/src/pages/websites/cert/index.tsx new file mode 100644 index 0000000..e11c70a --- /dev/null +++ b/src/pages/websites/cert/index.tsx @@ -0,0 +1,394 @@ +import { useTranslation } from '@/i18n.ts' +import { Button, Form, Popconfirm, Divider, Space, Tooltip, Tag, Input, Progress, Flex } from 'antd' +import { useAtom, useAtomValue } from 'jotai' +import { + algorithmTypes, + bandTypes, + certAtom, certsAtom, + certSearchAtom, + deleteCertAtom, + saveOrUpdateCertAtom, StatusText, +} from '@/store/websites/cert' +import { useEffect, useMemo, useState } from 'react' +import Action from '@/components/action/Action.tsx' +import { + BetaSchemaForm, + ProColumns, + ProFormColumnsType, +} from '@ant-design/pro-components' +import ListPageLayout from '@/layout/ListPageLayout.tsx' +import { useStyle } from './style' +import { CheckCircleFilled, ExclamationCircleFilled, } from '@ant-design/icons' +import { Table as ProTable } from '@/components/table' +import google from './assets/google.png' +import lets_encrypt from './assets/lets_encrypt.png' +import zerossl from './assets/zerossl.png' +import ModalPro from '@/components/modal-pro' + +const i18nPrefix = 'cert.list' + +const Cert = () => { + + const { styles, cx } = useStyle() + const { t } = useTranslation() + const [ form ] = Form.useForm() + const [ filterForm ] = Form.useForm() + const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCertAtom) + const [ search, setSearch ] = useAtom(certSearchAtom) + const [ currentCert, setCert ] = useAtom(certAtom) + const { data, isFetching, isLoading, refetch } = useAtomValue(certsAtom) + const { mutate: deleteCert, isPending: isDeleting } = useAtomValue(deleteCertAtom) + + const [ open, setOpen ] = useState(false) + const [ openFilter, setFilterOpen ] = useState(false) + const [ searchKey, setSearchKey ] = useState(search?.name) + + const columns = useMemo(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: { hidden: true } + }, + { + title: t(`${i18nPrefix}.columns.domains`, '域名'), + dataIndex: 'domains', + width: 350, + fieldProps: { + style: { width: '100%' }, + }, + formItemProps: { + rules: [ { required: true, message: t(`${i18nPrefix}.columns.domains.required`, '请输入域名') } ] + }, + renderFormItem: (_schema, config) => { + return + } + }, + { + title: t(`${i18nPrefix}.columns.type`, '域名验证'), + dataIndex: 'type', + hideInSearch: true, + align: 'center', + render: (_text, record) => { + if (record.type === 4) + return <> + return + } + }, + { + title: t(`${i18nPrefix}.columns.brand`, '证书品牌'), + dataIndex: 'brand', + valueType: 'select', + width: 100, + fieldProps: { + style: { width: '100%' }, + options: bandTypes + }, + render: (_text, record) => { + + if (record.brand === 'Google') + return + if (record.brand === 'ZeroSSL') + return + return + }, + formItemProps: { + rules: [ { required: true, message: t(`${i18nPrefix}.columns.brand.required`, '请选择证书品牌') } ] + } + }, + { + title: t(`${i18nPrefix}.columns.createTime`, '有效期(天)'), + dataIndex: 'createTime', + hideInSearch: true, + hideInForm: true, + width: 220, + render: (_text, record) => { + + const content = () => { + + if (record.lifeTime) + return { + return `${record.remainingTime}/${record.lifeTime}` + }}>{record.expire} + return ``}/> + } + return + +
生效时间:{record.notBefore}
+
失效时间:{record.notAfter}
+
创建时间:{record.createTime}
+ } + > + {content()} +
+
+ } + }, + { + title: t(`${i18nPrefix}.columns.algorithm`, '加密方式'), + dataIndex: 'algorithm', + valueType: 'select', + width: 100, + fieldProps: { + style: { width: '100%' }, + options: algorithmTypes, + }, + render: (text, record) => { + if (record.algorithm === 'ECC') + return {text} + return {text} + }, + formItemProps: { + rules: [ { required: true, message: t(`${i18nPrefix}.columns.algorithm.required`, '请选择加密方式') } ] + } + }, + + { + title: t(`${i18nPrefix}.columns.status`, '状态'), + dataIndex: 'status', + width: 100, + hideInSearch: true, + hideInForm: true, + render: (_text, record) => { + const [ text, color ] = StatusText[record.status] + return {text} + } + }, + { + title: t(`${i18nPrefix}.columns.remark`, '备注 '), + dataIndex: 'remark', + }, + { + title: t(`${i18nPrefix}.columns.option`, '操作'), + key: 'option', + width: 150, + valueType: 'option', + fixed: 'right', + render: (_, record) => [ + { + form.setFieldsValue(record) + setOpen(true) + }}>{t('actions.edit')}, + , + + {t(`actions.download`, '下载')} + , + , + { + deleteCert([ record.id ]) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + + ] + } + ] as ProColumns[] + }, [ isDeleting, currentCert, search ]) + + useEffect(() => { + + setSearchKey(search?.name) + filterForm.setFieldsValue(search) + + }, [ search ]) + + useEffect(() => { + if (isSuccess) { + setOpen(false) + } + }, [ isSuccess ]) + + return ( + + { + form.resetFields() + form.setFieldsValue({ + id: 0, + }) + setOpen(true) + }} + type={'primary'}>{t(`${i18nPrefix}.add`, '免费申请证明')} + + } + toolbar={{ + search: { + loading: isFetching && !!search?.name, + onSearch: (value: string) => { + setSearch(prev => ({ + ...prev, + name: value + })) + }, + allowClear: true, + onChange: (e) => { + setSearchKey(e.target?.value) + }, + value: searchKey, + placeholder: t(`${i18nPrefix}.placeholder`, '输入域名') + }, + actions: [] + }}/* + + + + + + ) + }, + + }} + onValuesChange={(values) => { + + }} + + onFinish={async (values) => { + //处理,变成数组 + Object.keys(values).forEach(key => { + if (typeof values[key] === 'string' && values[key].includes(',')) { + values[key] = values[key].split(',') + } + }) + + setSearch(values) + + }} + columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/> + + ) +} + +export default Cert \ No newline at end of file diff --git a/src/pages/websites/cert/style.ts b/src/pages/websites/cert/style.ts new file mode 100644 index 0000000..70e5394 --- /dev/null +++ b/src/pages/websites/cert/style.ts @@ -0,0 +1,26 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-domainGroup-list-page` + + const container = css` + .ant-table-cell{ + .ant-tag{ + padding-inline: 3px; + margin-inline-end: 3px; + } + } + .ant-table-empty { + .ant-table-body{ + height: calc(100vh - 350px) + } + } + .ant-pro-table-highlight{ + + } + ` + + return { + container: cx(prefix, props?.className, container), + } +}) \ No newline at end of file diff --git a/src/service/websites.ts b/src/service/websites.ts index 0faa708..581139f 100644 --- a/src/service/websites.ts +++ b/src/service/websites.ts @@ -6,6 +6,66 @@ import { IWebsiteDnsRecords } from '@/types/website/record' 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) + } + }, ssl: { ...createCURD('/website/ssl'), upload: async (params: WebSite.SSLUploadDto) => { @@ -33,22 +93,22 @@ const websitesServ = { domain: { ...createCURD('/website/domain'), //remark - remark: async (params: { id: string, remark: string }) => { + remark: async (params: { id: string, remark: string }) => { return request.post('/website/domain/remark', params) }, //tag - tag: async (params: { id: string, tags: string}) => { + tag: async (params: { id: string, tags: string }) => { return request.post('/website/domain/tag', params) }, //binding - binding: async (params: { id:string , user_id: string }) => { + binding: async (params: { id: string, user_id: string }) => { return request.post('/website/domain/binding', params) }, //group group: async (params: { id: string[], group_id: string }) => { return request.post('/website/domain/group', params) }, - describeDomainNS: async (params: { id: number }) => { + describeDomainNS: async (params: { id: number }) => { return request.post('/website/domain/describe_domain_ns', params) }, diff --git a/src/store/websites/cert.ts b/src/store/websites/cert.ts new file mode 100644 index 0000000..2eed857 --- /dev/null +++ b/src/store/websites/cert.ts @@ -0,0 +1,107 @@ +import { atom } from 'jotai' +import { IApiResult, IPage } from '@/global' +import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' +import { message } from 'antd' +import { t } from 'i18next' +import websitesServ from '@/service/websites.ts' + + +type SearchParams = IPage & { + name?: string +} + +export const bandTypes = [ + { label: 'Google', value: 'Google' }, + { label: 'ZeroSSL', value: 'ZeroSSL' }, + { label: 'Let\'s Encrypt', value: 'Let\'s Encrypt' }, +] + +export const algorithmTypes = [ + { label: 'RSA', value: 'RSA' }, + { label: 'ECC', value: 'ECC' }, +] + + +export const StatusText = { + 1: [ '已签发', 'green' ], + 2: [ '申请中', 'default' ], + 3: [ '申请失败', 'red' ] +} + + +export const certIdAtom = atom(0) + +export const certIdsAtom = atom([]) + +export const certAtom = atom(undefined as unknown as ICertificate) + +export const certSearchAtom = atom({ + // key: '', + pageSize: 10, + page: 1, +} as SearchParams) + +export const certPageAtom = atom({ + pageSize: 10, + page: 1, +}) + +export const certsAtom = atomWithQuery((get) => { + return { + queryKey: [ 'certs', get(certSearchAtom) ], + queryFn: async ({ queryKey: [ , params ] }) => { + return await websitesServ.cert.list(params as SearchParams) + }, + select: res => { + const data = res.data + data.rows = data.rows?.map(row => { + return { + ...row, + //status: convertToBool(row.status) + } + }) + return data + } + } +}) + +//saveOrUpdateAtom +export const saveOrUpdateCertAtom = atomWithMutation((get) => { + + return { + mutationKey: [ 'updateCert' ], + mutationFn: async (data) => { + //data.status = data.status ? '1' : '0' + if (data.id === 0) { + return await websitesServ.cert.add(data) + } + return await websitesServ.cert.update(data) + }, + onSuccess: (res) => { + const isAdd = !!res.data?.id + message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) + + //更新列表 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore fix + get(queryClientAtom).invalidateQueries({ queryKey: [ 'certs', get(certSearchAtom) ] }) + + return res + } + } +}) + +export const deleteCertAtom = atomWithMutation((get) => { + return { + mutationKey: [ 'deleteCert' ], + mutationFn: async (ids: number[]) => { + return await websitesServ.cert.batchDelete(ids ?? get(certIdsAtom)) + }, + onSuccess: (res) => { + message.success('message.deleteSuccess') + //更新列表 + get(queryClientAtom).invalidateQueries({ queryKey: [ 'certs', get(certSearchAtom) ] }) + return res + } + } +}) diff --git a/src/types/website/cert.d.ts b/src/types/website/cert.d.ts new file mode 100644 index 0000000..b2fbd43 --- /dev/null +++ b/src/types/website/cert.d.ts @@ -0,0 +1,20 @@ +interface ICertificate { + id: number; + brand: string; + domains: string; + type: number; + level: number; + status: number; + createTime: string; + notAfter: string; + notBefore: string; + version: number; + sigAlgName: string; + remark: string; + name: string; + lifeTime: number; + remainingTime: number; + algorithm: string; + bit: number; + fromType: number; +} \ No newline at end of file