From 54075f8d78e44294b6949f2d6ab6970e8a2f4f15 Mon Sep 17 00:00:00 2001 From: dark Date: Sun, 5 May 2024 01:07:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=87=AA=E7=AD=BE=E8=AF=81?= =?UTF-8?q?=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/websites/ssl/acme/AcmeList.tsx | 171 ++++++++++++++ src/pages/websites/ssl/ca/CAList.tsx | 245 ++++++++++++++++++++ src/pages/websites/ssl/ca/Detail.tsx | 116 ++++++++++ src/pages/websites/ssl/ca/SelfSign.tsx | 136 ++++++++++++ src/pages/websites/ssl/ca/store.ts | 20 ++ src/pages/websites/ssl/ca/style.ts | 23 ++ src/pages/websites/ssl/components/AcmeList.tsx | 171 -------------- src/pages/websites/ssl/components/CAList.tsx | 221 ------------------ src/pages/websites/ssl/components/DNSList.tsx | 296 ------------------------- src/pages/websites/ssl/dns/DNSList.tsx | 296 +++++++++++++++++++++++++ src/pages/websites/ssl/index.tsx | 40 ++-- src/service/websites.ts | 15 +- src/store/websites/acme.ts | 8 +- src/store/websites/ca.ts | 15 ++ src/store/websites/ssl.ts | 21 +- src/types/index.d.ts | 2 +- src/types/website/ca.d.ts | 13 ++ 17 files changed, 1096 insertions(+), 713 deletions(-) create mode 100644 src/pages/websites/ssl/acme/AcmeList.tsx create mode 100644 src/pages/websites/ssl/ca/CAList.tsx create mode 100644 src/pages/websites/ssl/ca/Detail.tsx create mode 100644 src/pages/websites/ssl/ca/SelfSign.tsx create mode 100644 src/pages/websites/ssl/ca/store.ts create mode 100644 src/pages/websites/ssl/ca/style.ts delete mode 100644 src/pages/websites/ssl/components/AcmeList.tsx delete mode 100644 src/pages/websites/ssl/components/CAList.tsx delete mode 100644 src/pages/websites/ssl/components/DNSList.tsx create mode 100644 src/pages/websites/ssl/dns/DNSList.tsx diff --git a/src/pages/websites/ssl/acme/AcmeList.tsx b/src/pages/websites/ssl/acme/AcmeList.tsx new file mode 100644 index 0000000..1f685c0 --- /dev/null +++ b/src/pages/websites/ssl/acme/AcmeList.tsx @@ -0,0 +1,171 @@ +import { useMemo, useState } from 'react' +import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' +import { useTranslation } from '@/i18n.ts' +import { AcmeAccountTypes, acmeListAtom, acmePageAtom, AcmeType, saveOrUpdateAcmeAtom } from '@/store/websites/acme.ts' +import { useAtom, useAtomValue } from 'jotai' +import { Alert, Button, Form, Popconfirm } from 'antd' +import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts' +import { deleteDNSAtom } from '@/store/websites/dns.ts' +import { WebSite } from '@/types' + +const AcmeList = () => { + + const { t } = useTranslation() + const [ form ] = Form.useForm() + const [ page, setPage ] = useAtom(acmePageAtom) + const { data, isLoading, refetch } = useAtomValue(acmeListAtom) + const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateAcmeAtom) + const { mutate: deleteDNS, isPending: isDeleting } = useAtomValue(deleteDNSAtom) + const [ open, setOpen ] = useState(false) + + const columns = useMemo[]>(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + formItemProps: { + hidden: true, + } + }, + { + title: t('website.ssl.acme.columns.email', '邮箱'), + dataIndex: 'email', + valueType: 'text', + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + { + title: t('website.ssl.acme.columns.type', '帐号类型'), + dataIndex: 'type', + valueType: 'select', + fieldProps: { + options: AcmeAccountTypes + }, + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请选择') } + ] + } + }, + { + title: t('website.ssl.acme.columns.keyType', '密钥算法'), + dataIndex: 'keyType', + valueType: 'select', + fieldProps: { + options: KeyTypes + }, + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请选择') } + ] + }, + }, + { + title: t('website.ssl.acme.columns.url', 'URL'), + dataIndex: 'url', + valueType: 'text', + ellipsis: true, // 文本溢出省略 + hideInForm: true, + }, { + title: '操作', + valueType: 'option', + render: (_, record) => { + return [ + { + deleteDNS(record.id) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + + ] + } + } + ] + }, []) + + return ( + <> + + + cardProps={{ + bodyStyle: { + padding: 0, + } + }} + rowKey="id" + headerTitle={ + + } + loading={isLoading} + dataSource={data?.rows ?? []} + columns={columns} + search={false} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: page.pageSize, + current: page.page, + onChange: (current, pageSize) => { + setPage(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + + }} + /> + + shouldUpdate={false} + width={600} + form={form} + layout={'horizontal'} + scrollToFirstError={true} + title={t(`website.ssl.acme.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '证书编辑' : '证书添加')} + // colProps={{ span: 24 }} + labelCol={{ span: 6 }} + wrapperCol={{ span: 14 }} + layoutType={'ModalForm'} + open={open} + modalProps={{ + maskClosable: false, + }} + onOpenChange={(open) => { + setOpen(open) + }} + loading={isSubmitting} + onFinish={async (values) => { + // console.log('values', values) + saveOrUpdate(values) + return isSuccess + }} + columns={columns as ProFormColumnsType[]}/> + + ) +} + +export default AcmeList \ No newline at end of file diff --git a/src/pages/websites/ssl/ca/CAList.tsx b/src/pages/websites/ssl/ca/CAList.tsx new file mode 100644 index 0000000..c503334 --- /dev/null +++ b/src/pages/websites/ssl/ca/CAList.tsx @@ -0,0 +1,245 @@ +import { useMemo, useState } from 'react' +import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' +import { useTranslation } from '@/i18n.ts' +import { deleteCaAtom } from '@/store/websites/ca.ts' +import { useAtom, useAtomValue, useSetAtom } from 'jotai' +import { Button, Form, Popconfirm } from 'antd' +import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts' +import { caListAtom, caPageAtom, saveOrUpdateCaAtom } from '@/store/websites/ca.ts' +import { WebSite } from '@/types' +import Detail from './Detail.tsx' +import { detailAtom, selfSignAtom } from './store.ts' +import SelfSign from './SelfSign.tsx' + +const CAList = () => { + + const { t } = useTranslation() + const [ form ] = Form.useForm() + const [ page, setPage ] = useAtom(caPageAtom) + const { data, isLoading, refetch } = useAtomValue(caListAtom) + const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCaAtom) + const { mutate: deleteCA, isPending: isDeleting } = useAtomValue(deleteCaAtom) + const [ open, setOpen ] = useState(false) + const updateUI = useSetAtom(detailAtom) + const selfSignUI = useSetAtom(selfSignAtom) + const columns = useMemo[]>(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + formItemProps: { + hidden: true, + } + }, + { + title: t('website.ssl.ca.columns.name', '名称'), + dataIndex: 'name', + valueType: 'text', + formItemProps: { + label: t('website.ssl.ca.form.name', '机构名称'), + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + { + title: t('website.ssl.ca.form.common_name', '证书主体名称(CN)'), + dataIndex: 'common_name', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + + { + title: t('website.ssl.ca.form.organization', '公司/组织'), + dataIndex: 'organization', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + + { + title: t('website.ssl.ca.form.organization_uint', '部门'), + dataIndex: 'organization_uint', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: {} + }, + { + title: t('website.ssl.ca.form.country', '国家代号'), + dataIndex: 'country', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + { + title: t('website.ssl.ca.form.province', '省份'), + dataIndex: 'province', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: {} + }, + { + title: t('website.ssl.ca.form.city', '城市'), + dataIndex: 'city', + hideInTable: true, + hideInSetting: true, + valueType: 'text', + formItemProps: {} + }, + + { + title: t('website.ssl.ca.columns.keyType', '密钥算法'), + dataIndex: 'key_type', + valueType: 'select', + fieldProps: { + options: KeyTypes + }, + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请选择') } + ] + }, + }, + { + title: t('website.ssl.ca.columns.createAt', '时间'), + dataIndex: 'created_at', + valueType: 'dateTime', + hideInForm: true, + }, + { + title: '操作', + valueType: 'option', + render: (_, record) => { + return [ + { + selfSignUI({ + open: true, + record: { + id: record.id, + key_type: KeyTypeEnum.EC256, + unit: 'year', + auto_renew: true, + time: 10, + } + }) + }}>{t('website.actions.selfSign', '签发证书')}, + { + updateUI({ + open: true, record, + }) + }}>{t('website.actions.detail', '详情')}, + + { + deleteCA(record.id) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + + ] + } + } + ] + }, []) + + return ( + <> + + cardProps={{ + bodyStyle: { + padding: 0, + } + }} + rowKey="id" + headerTitle={ + + } + loading={isLoading} + dataSource={data?.rows ?? []} + columns={columns} + search={false} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: page.pageSize, + current: page.page, + onChange: (current, pageSize) => { + setPage(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + + }} + /> + + shouldUpdate={false} + width={600} + form={form} + layout={'horizontal'} + scrollToFirstError={true} + title={t(`website.ssl.ca.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '证书编辑' : '证书添加')} + // colProps={{ span: 24 }} + labelCol={{ span: 6 }} + wrapperCol={{ span: 14 }} + layoutType={'ModalForm'} + open={open} + modalProps={{ + maskClosable: false, + }} + onOpenChange={(open) => { + setOpen(open) + }} + loading={isSubmitting} + onFinish={async (values) => { + // console.log('values', values) + saveOrUpdate(values) + return isSuccess + }} + columns={columns as ProFormColumnsType[]}/> + + + + ) +} + +export default CAList \ No newline at end of file diff --git a/src/pages/websites/ssl/ca/Detail.tsx b/src/pages/websites/ssl/ca/Detail.tsx new file mode 100644 index 0000000..b214aa9 --- /dev/null +++ b/src/pages/websites/ssl/ca/Detail.tsx @@ -0,0 +1,116 @@ +import { Segmented, Drawer, DrawerProps, Input, Button, Flex, message, Descriptions } from 'antd' +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from '@/i18n.ts' +import { useAtom } from 'jotai' +import { detailAtom } from './store.ts' +import { useStyle } from './style.ts' +import { useCopyToClipboard } from 'react-use' + +const Detail = (props: DrawerProps) => { + + + const prefix = 'website.ssl.ca.detail' + const { t } = useTranslation() + const { styles } = useStyle() + const [ key, setKey ] = useState('base') + const [ ui, setUI ] = useAtom(detailAtom) + const [ copyState, setCopy ] = useCopyToClipboard() + const options = useMemo(() => { + return [ + { label: t(`${prefix}.base`, '机构详情'), value: 'base' }, + { label: t(`${prefix}.src`, 'src'), value: 'csr' }, + { label: t(`${prefix}.private_key`, '私钥'), value: 'private_key' }, + ] + }, []) + + useEffect(() => { + if (copyState.error) { + message.error(t('message.copyError', '复制失败')) + } else if (copyState.value) { + message.success(t('message.copySuccess', '复制成功')) + } + }, [ copyState ]) + + const render = useMemo(() => { + switch (key) { + case 'base': + return
+ + + {ui.record?.name} + + + {ui.record?.common_name} + + + {ui.record?.organization} + + + {ui.record?.organizationUint} + + + {ui.record?.country} + + + {ui.record?.province} + + + {ui.record?.city} + + +
+ case 'csr': + return + + + + + + case 'private_key': + return + + + + + + default: + return null + } + }, [ key, ui.record ]) + + return ( + + setUI(prev => ({ + ...prev, + open: false + })) + } + > + +
+ {render} +
+
+ ) +} + +export default Detail \ No newline at end of file diff --git a/src/pages/websites/ssl/ca/SelfSign.tsx b/src/pages/websites/ssl/ca/SelfSign.tsx new file mode 100644 index 0000000..6e9c0c1 --- /dev/null +++ b/src/pages/websites/ssl/ca/SelfSign.tsx @@ -0,0 +1,136 @@ +import { Form, Input, InputNumber, Modal, ModalProps, Select, Space, Switch } from 'antd' +import { WebSite } from '@/types' +import { useTranslation } from '@/i18n.ts' +import { useAtom, useAtomValue } from 'jotai' +import { selfSignAtom } from './store.ts' +import { KeyTypes } from '@/store/websites/ssl.ts' +import { useEffect } from 'react' +import { obtainSslAtom } from '@/store/websites/ca.ts' + +const SelfSign = (props: ModalProps) => { + const prefix = 'website.ssl.ca.selfSign' + const [ form ] = Form.useForm() + const { t } = useTranslation() + const [ ui, setUI ] = useAtom(selfSignAtom) + const { mutate, isPending, isSuccess } = useAtomValue(obtainSslAtom) + + useEffect(() => { + form.setFieldsValue(ui.record) + }, [ ui.open ]) + + return ( + { + setUI({ open: false, record: {} }) + }} + confirmLoading={isPending} + onOk={() => { + form.validateFields().then(() => { + mutate(ui.record) + }) + return isSuccess + }} + > + + form={form} + labelCol={{ span: 6 }} + wrapperCol={{ span: 14 }} + onValuesChange={value => { + setUI(prev => { + return { + ...prev, + record: { + ...prev.record, + ...value, + }, + } + }) + }} + > + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default SelfSign \ No newline at end of file diff --git a/src/pages/websites/ssl/ca/store.ts b/src/pages/websites/ssl/ca/store.ts new file mode 100644 index 0000000..bc690fe --- /dev/null +++ b/src/pages/websites/ssl/ca/store.ts @@ -0,0 +1,20 @@ +import { atom } from 'jotai' +import { WebSite } from '@/types' + +type DetailUI = { + open: boolean, + record?: WebSite.ICA, + +} +type SelfSignUI = { + open: boolean, + record?: WebSite.ISSLObtainByCA, +} +export const detailAtom = atom({ + open: false, +}) + +export const selfSignAtom = atom({ + open: false, +}) + diff --git a/src/pages/websites/ssl/ca/style.ts b/src/pages/websites/ssl/ca/style.ts new file mode 100644 index 0000000..cf59121 --- /dev/null +++ b/src/pages/websites/ssl/ca/style.ts @@ -0,0 +1,23 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-ca-detail-page` + + const container = css` + + ` + + const content = css` + padding: 20px 0; + + .ant-descriptions-item-label{ + font-weight: bold; + width: 50%; + } + ` + + return { + container: cx(prefix, props?.className, container), + content, + } +}) \ No newline at end of file diff --git a/src/pages/websites/ssl/components/AcmeList.tsx b/src/pages/websites/ssl/components/AcmeList.tsx deleted file mode 100644 index 1f685c0..0000000 --- a/src/pages/websites/ssl/components/AcmeList.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { useMemo, useState } from 'react' -import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' -import { useTranslation } from '@/i18n.ts' -import { AcmeAccountTypes, acmeListAtom, acmePageAtom, AcmeType, saveOrUpdateAcmeAtom } from '@/store/websites/acme.ts' -import { useAtom, useAtomValue } from 'jotai' -import { Alert, Button, Form, Popconfirm } from 'antd' -import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts' -import { deleteDNSAtom } from '@/store/websites/dns.ts' -import { WebSite } from '@/types' - -const AcmeList = () => { - - const { t } = useTranslation() - const [ form ] = Form.useForm() - const [ page, setPage ] = useAtom(acmePageAtom) - const { data, isLoading, refetch } = useAtomValue(acmeListAtom) - const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateAcmeAtom) - const { mutate: deleteDNS, isPending: isDeleting } = useAtomValue(deleteDNSAtom) - const [ open, setOpen ] = useState(false) - - const columns = useMemo[]>(() => { - return [ - { - title: 'ID', - dataIndex: 'id', - hideInTable: true, - formItemProps: { - hidden: true, - } - }, - { - title: t('website.ssl.acme.columns.email', '邮箱'), - dataIndex: 'email', - valueType: 'text', - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - { - title: t('website.ssl.acme.columns.type', '帐号类型'), - dataIndex: 'type', - valueType: 'select', - fieldProps: { - options: AcmeAccountTypes - }, - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请选择') } - ] - } - }, - { - title: t('website.ssl.acme.columns.keyType', '密钥算法'), - dataIndex: 'keyType', - valueType: 'select', - fieldProps: { - options: KeyTypes - }, - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请选择') } - ] - }, - }, - { - title: t('website.ssl.acme.columns.url', 'URL'), - dataIndex: 'url', - valueType: 'text', - ellipsis: true, // 文本溢出省略 - hideInForm: true, - }, { - title: '操作', - valueType: 'option', - render: (_, record) => { - return [ - { - deleteDNS(record.id) - }} - title={t('message.deleteConfirm')}> - - {t('actions.delete', '删除')} - - - ] - } - } - ] - }, []) - - return ( - <> - - - cardProps={{ - bodyStyle: { - padding: 0, - } - }} - rowKey="id" - headerTitle={ - - } - loading={isLoading} - dataSource={data?.rows ?? []} - columns={columns} - search={false} - options={{ - reload: () => { - refetch() - }, - }} - pagination={{ - total: data?.total, - pageSize: page.pageSize, - current: page.page, - onChange: (current, pageSize) => { - setPage(prev => { - return { - ...prev, - page: current, - pageSize: pageSize, - } - }) - }, - - }} - /> - - shouldUpdate={false} - width={600} - form={form} - layout={'horizontal'} - scrollToFirstError={true} - title={t(`website.ssl.acme.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '证书编辑' : '证书添加')} - // colProps={{ span: 24 }} - labelCol={{ span: 6 }} - wrapperCol={{ span: 14 }} - layoutType={'ModalForm'} - open={open} - modalProps={{ - maskClosable: false, - }} - onOpenChange={(open) => { - setOpen(open) - }} - loading={isSubmitting} - onFinish={async (values) => { - // console.log('values', values) - saveOrUpdate(values) - return isSuccess - }} - columns={columns as ProFormColumnsType[]}/> - - ) -} - -export default AcmeList \ No newline at end of file diff --git a/src/pages/websites/ssl/components/CAList.tsx b/src/pages/websites/ssl/components/CAList.tsx deleted file mode 100644 index e0ded30..0000000 --- a/src/pages/websites/ssl/components/CAList.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import { useMemo, useState } from 'react' -import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' -import { useTranslation } from '@/i18n.ts' -import { deleteCaAtom } from '@/store/websites/ca.ts' -import { useAtom, useAtomValue } from 'jotai' -import { Button, Form, Popconfirm } from 'antd' -import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts' -import { caListAtom, caPageAtom, saveOrUpdateCaAtom } from '@/store/websites/ca.ts' -import { WebSite } from '@/types' - -const CAList = () => { - - const { t } = useTranslation() - const [ form ] = Form.useForm() - const [ page, setPage ] = useAtom(caPageAtom) - const { data, isLoading, refetch } = useAtomValue(caListAtom) - const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCaAtom) - const { mutate: deleteCA, isPending: isDeleting } = useAtomValue(deleteCaAtom) - const [ open, setOpen ] = useState(false) - - const columns = useMemo[]>(() => { - return [ - { - title: 'ID', - dataIndex: 'id', - hideInTable: true, - formItemProps: { - hidden: true, - } - }, - { - title: t('website.ssl.ca.columns.name', '名称'), - dataIndex: 'name', - valueType: 'text', - formItemProps: { - label: t('website.ssl.ca.form.name', '机构名称'), - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - { - title: t('website.ssl.ca.form.common_name', '证书主体名称(CN)'), - dataIndex: 'common_name', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - - { - title: t('website.ssl.ca.form.organization', '公司/组织'), - dataIndex: 'organization', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - - { - title: t('website.ssl.ca.form.organization_uint', '部门'), - dataIndex: 'organization_uint', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: {} - }, - { - title: t('website.ssl.ca.form.country', '国家代号'), - dataIndex: 'country', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - { - title: t('website.ssl.ca.form.province', '省份'), - dataIndex: 'province', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: {} - }, - { - title: t('website.ssl.ca.form.city', '城市'), - dataIndex: 'city', - hideInTable: true, - hideInSetting: true, - valueType: 'text', - formItemProps: {} - }, - - { - title: t('website.ssl.ca.columns.keyType', '密钥算法'), - dataIndex: 'keyType', - valueType: 'select', - fieldProps: { - options: KeyTypes - }, - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请选择') } - ] - }, - }, - { - title: t('website.ssl.ca.columns.createAt', '时间'), - dataIndex: 'create_at', - valueType: 'dateTime', - hideInForm: true, - }, - { - title: '操作', - valueType: 'option', - render: (_, record) => { - return [ - { - deleteCA(record.id) - }} - title={t('message.deleteConfirm')}> - - {t('actions.delete', '删除')} - - - ] - } - } - ] - }, []) - - return ( - <> - - cardProps={{ - bodyStyle: { - padding: 0, - } - }} - rowKey="id" - headerTitle={ - - } - loading={isLoading} - dataSource={data?.rows ?? []} - columns={columns} - search={false} - options={{ - reload: () => { - refetch() - }, - }} - pagination={{ - total: data?.total, - pageSize: page.pageSize, - current: page.page, - onChange: (current, pageSize) => { - setPage(prev => { - return { - ...prev, - page: current, - pageSize: pageSize, - } - }) - }, - - }} - /> - - shouldUpdate={false} - width={600} - form={form} - layout={'horizontal'} - scrollToFirstError={true} - title={t(`website.ssl.ca.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '证书编辑' : '证书添加')} - // colProps={{ span: 24 }} - labelCol={{ span: 6 }} - wrapperCol={{ span: 14 }} - layoutType={'ModalForm'} - open={open} - modalProps={{ - maskClosable: false, - }} - onOpenChange={(open) => { - setOpen(open) - }} - loading={isSubmitting} - onFinish={async (values) => { - // console.log('values', values) - saveOrUpdate(values) - return isSuccess - }} - columns={columns as ProFormColumnsType[]}/> - - ) -} - -export default CAList \ No newline at end of file diff --git a/src/pages/websites/ssl/components/DNSList.tsx b/src/pages/websites/ssl/components/DNSList.tsx deleted file mode 100644 index ef25654..0000000 --- a/src/pages/websites/ssl/components/DNSList.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import { useMemo, useState } from 'react' -import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' -import { useTranslation } from '@/i18n.ts' -import { useAtom, useAtomValue } from 'jotai' -import { Button, Form, Popconfirm } from 'antd' -import { - deleteDNSAtom, - dnsListAtom, - dnsPageAtom, - DNSTypeEnum, - DNSTypes, - saveOrUpdateDNSAtom -} from '@/store/websites/dns.ts' -import { WebSite } from '@/types' - -const getKeyColumn = (type: string, t) => { - const columns: ProColumns[] = [] - switch (type) { - case DNSTypeEnum.AliYun: { - columns.push(...[ - { - title: t('website.ssl.dns.columns.accessKey', 'Access Key'), - dataIndex: 'accessKey', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - { - title: t('website.ssl.dns.columns.secretKey', 'Secret Key'), - dataIndex: 'secretKey', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - ]) - } - break - case DNSTypeEnum.TencentCloud: { - columns.push(...[ - { - title: t('website.ssl.dns.columns.secretID', 'Secret ID'), - dataIndex: 'secretID', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, { - title: t('website.ssl.dns.columns.secretKey', 'Secret Key'), - dataIndex: 'secretKey', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - ]) - break - } - case DNSTypeEnum.DnsPod: { - columns.push(...[ - { - title: t('website.ssl.dns.columns.apiId', 'ID'), - dataIndex: 'apiId', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, { - title: t('website.ssl.dns.columns.token', 'Token'), - dataIndex: 'token', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - ]) - break - } - case DNSTypeEnum.CloudFlare: { - columns.push(...[ - { - title: t('website.ssl.dns.columns.email', 'Email'), - dataIndex: 'email', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, { - title: t('website.ssl.dns.columns.apiKey', 'API ToKen'), - dataIndex: 'apiKey', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - ] - ) - break - } - case DNSTypeEnum.Godaddy: - case DNSTypeEnum.NameCheap: - case DNSTypeEnum.NameSilo: - columns.push({ - title: t('website.ssl.dns.columns.apiKey', 'API Key'), - dataIndex: 'apiKey', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - ) - if (type === DNSTypeEnum.NameCheap) { - columns.push({ - title: t('website.ssl.dns.columns.apiUser', 'API User'), - dataIndex: 'apiUser', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }) - } else if (type === DNSTypeEnum.Godaddy) { - columns.push({ - title: t('website.ssl.dns.columns.apiSecret', 'API Secret'), - dataIndex: 'apiSecret', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }) - } - break - case DNSTypeEnum.NameCom: { - columns.push( - { - title: t('website.ssl.dns.columns.apiUser', 'UserName'), - dataIndex: 'apiUser', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - }, - { - title: t('website.ssl.dns.columns.token', 'Token'), - dataIndex: 'token', - formItemProps: { - rules: [ { required: true, message: t('message.required') } ] - } - } - ) - break - } - default: - break - - } - return columns -} - -const DNSList = () => { - - const { t } = useTranslation() - const [ form ] = Form.useForm() - const [ page, setPage ] = useAtom(dnsPageAtom) - const { data, isLoading, refetch } = useAtomValue(dnsListAtom) - const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateDNSAtom) - const { mutate: deleteDNS, isPending: isDeleting } = useAtomValue(deleteDNSAtom) - const [ open, setOpen ] = useState(false) - - const columns = useMemo[]>(() => { - return [ - { - title: 'ID', - dataIndex: 'id', - hideInTable: true, - formItemProps: { - hidden: true, - } - }, - { - title: t('website.ssl.dns.columns.name', '名称'), - dataIndex: 'name', - valueType: 'text', - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请输入') } - ] - } - }, - - { - title: t('website.ssl.dns.columns.type', '类型'), - dataIndex: 'type', - valueType: 'select', - fieldProps: { - options: DNSTypes - }, - formItemProps: { - rules: [ - { required: true, message: t('message.required', '请选择') } - ] - }, - }, - { - name: [ 'type' ], - valueType: 'dependency', - hideInSetting: true, - columns: ({ type }) => { - return getKeyColumn(type, t) - } - }, - { - title: '操作', - valueType: 'option', - render: (_, record) => { - return [ - { - deleteDNS(record.id) - }} - title={t('message.deleteConfirm')}> - - {t('actions.delete', '删除')} - - - ] - } - } - ] - }, []) - - return ( - <> - - cardProps={{ - bodyStyle: { - padding: 0, - } - }} - rowKey="id" - headerTitle={ - - } - loading={isLoading} - dataSource={data?.rows ?? []} - columns={columns} - search={false} - options={{ - reload: () => { - refetch() - }, - }} - pagination={{ - total: data?.total, - pageSize: page.pageSize, - current: page.page, - onChange: (current, pageSize) => { - setPage(prev => { - return { - ...prev, - page: current, - pageSize: pageSize, - } - }) - }, - - }} - /> - - shouldUpdate={false} - width={600} - form={form} - layout={'horizontal'} - scrollToFirstError={true} - title={t(`website.ssl.dns.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? 'DNS帐号编辑' : 'DNS帐号添加')} - // colProps={{ span: 24 }} - labelCol={{ span: 6 }} - wrapperCol={{ span: 14 }} - layoutType={'ModalForm'} - open={open} - modalProps={{ - maskClosable: false, - }} - onOpenChange={(open) => { - setOpen(open) - }} - loading={isSubmitting} - onFinish={async (values) => { - // console.log('values', values) - saveOrUpdate(values) - return isSuccess - }} - columns={columns as ProFormColumnsType[]}/> - - ) -} - -export default DNSList \ No newline at end of file diff --git a/src/pages/websites/ssl/dns/DNSList.tsx b/src/pages/websites/ssl/dns/DNSList.tsx new file mode 100644 index 0000000..ef25654 --- /dev/null +++ b/src/pages/websites/ssl/dns/DNSList.tsx @@ -0,0 +1,296 @@ +import { useMemo, useState } from 'react' +import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' +import { useTranslation } from '@/i18n.ts' +import { useAtom, useAtomValue } from 'jotai' +import { Button, Form, Popconfirm } from 'antd' +import { + deleteDNSAtom, + dnsListAtom, + dnsPageAtom, + DNSTypeEnum, + DNSTypes, + saveOrUpdateDNSAtom +} from '@/store/websites/dns.ts' +import { WebSite } from '@/types' + +const getKeyColumn = (type: string, t) => { + const columns: ProColumns[] = [] + switch (type) { + case DNSTypeEnum.AliYun: { + columns.push(...[ + { + title: t('website.ssl.dns.columns.accessKey', 'Access Key'), + dataIndex: 'accessKey', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + { + title: t('website.ssl.dns.columns.secretKey', 'Secret Key'), + dataIndex: 'secretKey', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + ]) + } + break + case DNSTypeEnum.TencentCloud: { + columns.push(...[ + { + title: t('website.ssl.dns.columns.secretID', 'Secret ID'), + dataIndex: 'secretID', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, { + title: t('website.ssl.dns.columns.secretKey', 'Secret Key'), + dataIndex: 'secretKey', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + ]) + break + } + case DNSTypeEnum.DnsPod: { + columns.push(...[ + { + title: t('website.ssl.dns.columns.apiId', 'ID'), + dataIndex: 'apiId', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, { + title: t('website.ssl.dns.columns.token', 'Token'), + dataIndex: 'token', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + ]) + break + } + case DNSTypeEnum.CloudFlare: { + columns.push(...[ + { + title: t('website.ssl.dns.columns.email', 'Email'), + dataIndex: 'email', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, { + title: t('website.ssl.dns.columns.apiKey', 'API ToKen'), + dataIndex: 'apiKey', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + ] + ) + break + } + case DNSTypeEnum.Godaddy: + case DNSTypeEnum.NameCheap: + case DNSTypeEnum.NameSilo: + columns.push({ + title: t('website.ssl.dns.columns.apiKey', 'API Key'), + dataIndex: 'apiKey', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + ) + if (type === DNSTypeEnum.NameCheap) { + columns.push({ + title: t('website.ssl.dns.columns.apiUser', 'API User'), + dataIndex: 'apiUser', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }) + } else if (type === DNSTypeEnum.Godaddy) { + columns.push({ + title: t('website.ssl.dns.columns.apiSecret', 'API Secret'), + dataIndex: 'apiSecret', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }) + } + break + case DNSTypeEnum.NameCom: { + columns.push( + { + title: t('website.ssl.dns.columns.apiUser', 'UserName'), + dataIndex: 'apiUser', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + }, + { + title: t('website.ssl.dns.columns.token', 'Token'), + dataIndex: 'token', + formItemProps: { + rules: [ { required: true, message: t('message.required') } ] + } + } + ) + break + } + default: + break + + } + return columns +} + +const DNSList = () => { + + const { t } = useTranslation() + const [ form ] = Form.useForm() + const [ page, setPage ] = useAtom(dnsPageAtom) + const { data, isLoading, refetch } = useAtomValue(dnsListAtom) + const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateDNSAtom) + const { mutate: deleteDNS, isPending: isDeleting } = useAtomValue(deleteDNSAtom) + const [ open, setOpen ] = useState(false) + + const columns = useMemo[]>(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + formItemProps: { + hidden: true, + } + }, + { + title: t('website.ssl.dns.columns.name', '名称'), + dataIndex: 'name', + valueType: 'text', + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请输入') } + ] + } + }, + + { + title: t('website.ssl.dns.columns.type', '类型'), + dataIndex: 'type', + valueType: 'select', + fieldProps: { + options: DNSTypes + }, + formItemProps: { + rules: [ + { required: true, message: t('message.required', '请选择') } + ] + }, + }, + { + name: [ 'type' ], + valueType: 'dependency', + hideInSetting: true, + columns: ({ type }) => { + return getKeyColumn(type, t) + } + }, + { + title: '操作', + valueType: 'option', + render: (_, record) => { + return [ + { + deleteDNS(record.id) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + + ] + } + } + ] + }, []) + + return ( + <> + + cardProps={{ + bodyStyle: { + padding: 0, + } + }} + rowKey="id" + headerTitle={ + + } + loading={isLoading} + dataSource={data?.rows ?? []} + columns={columns} + search={false} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: page.pageSize, + current: page.page, + onChange: (current, pageSize) => { + setPage(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + + }} + /> + + shouldUpdate={false} + width={600} + form={form} + layout={'horizontal'} + scrollToFirstError={true} + title={t(`website.ssl.dns.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? 'DNS帐号编辑' : 'DNS帐号添加')} + // colProps={{ span: 24 }} + labelCol={{ span: 6 }} + wrapperCol={{ span: 14 }} + layoutType={'ModalForm'} + open={open} + modalProps={{ + maskClosable: false, + }} + onOpenChange={(open) => { + setOpen(open) + }} + loading={isSubmitting} + onFinish={async (values) => { + // console.log('values', values) + saveOrUpdate(values) + return isSuccess + }} + columns={columns as ProFormColumnsType[]}/> + + ) +} + +export default DNSList \ No newline at end of file diff --git a/src/pages/websites/ssl/index.tsx b/src/pages/websites/ssl/index.tsx index 13a2dce..8f77f2b 100644 --- a/src/pages/websites/ssl/index.tsx +++ b/src/pages/websites/ssl/index.tsx @@ -1,6 +1,6 @@ import { useAtom, useAtomValue } from 'jotai' import { - deleteSslAtom, + deleteSslAtom, getProvider, KeyTypeEnum, KeyTypes, ProviderTypeEnum, @@ -16,12 +16,13 @@ import { useTranslation } from '@/i18n.ts' import { Button, Form, Popconfirm } from 'antd' import { PlusOutlined } from '@ant-design/icons' import DrawerPicker from '@/components/drawer-picker/DrawerPicker.tsx' -import AcmeList from './components/AcmeList.tsx' +import AcmeList from './acme/AcmeList.tsx' import { acmeListAtom, AcmeType, getAcmeAccountTypeName } from '@/store/websites/acme.ts' import { dnsListAtom, getDNSTypeName } from '@/store/websites/dns.ts' -import DNSList from './components/DNSList.tsx' -import CAList from '@/pages/websites/ssl/components/CAList.tsx' +import DNSList from './dns/DNSList.tsx' +import CAList from './ca/CAList.tsx' import { WebSite } from '@/types' +import Switch from '@/components/switch' const SSL = () => { @@ -51,7 +52,7 @@ const SSL = () => { }, { title: t('website.ssl.columns.primaryDomain', '域名'), - dataIndex: 'primaryDomain', + dataIndex: 'primary_domain', formItemProps: { label: t('website.ssl.form.primaryDomain', '主域名'), rules: [ { required: true, message: t('message.required', '主域名') } ] @@ -59,11 +60,11 @@ const SSL = () => { }, { title: t('website.ssl.columns.otherDomains', '其它域名'), - dataIndex: 'otherDomains', + dataIndex: 'domains', }, { title: t('website.ssl.columns.acmeAccountId', 'Acme帐号'), - dataIndex: 'acmeAccountId', + dataIndex: 'acme_account_id', valueType: 'select', fieldProps: { loading: acmeLoading, @@ -80,7 +81,7 @@ const SSL = () => { }, { title: t('website.ssl.columns.keyType', '密钥算法'), - dataIndex: 'keyType', + dataIndex: 'key_type', hideInTable: true, valueType: 'select', fieldProps: { @@ -96,7 +97,6 @@ const SSL = () => { title: t('website.ssl.columns.provider', '申请方式'), dataIndex: 'provider', valueType: 'radio', - initialValue: ProviderTypeEnum.DnsAccount, valueEnum: { [ProviderTypeEnum.DnsAccount]: { text: t('website.ssl.providerTypeEnum.DnsAccount', 'DNS帐号'), @@ -109,6 +109,9 @@ const SSL = () => { } }, dependencies: [ 'provider' ], + renderText: (text) => { + return getProvider(text) + }, formItemProps: (form, config) => { const val = form.getFieldValue(config.dataIndex) const help = { @@ -127,11 +130,12 @@ const SSL = () => { name: [ 'provider' ], valueType: 'dependency', hideInSetting: true, + hideInTable: true, columns: ({ provider }) => { if (provider === ProviderTypeEnum.DnsAccount) { return [ { title: t('website.ssl.columns.dnsAccountId', 'DNS帐号'), - dataIndex: 'dnsAccountId', + dataIndex: 'dns_account_id', valueType: 'select', formItemProps: { rules: [ { required: true, message: t('message.required', '请输入DNS帐号') } ] @@ -151,22 +155,25 @@ const SSL = () => { }, { title: t('website.ssl.columns.autoRenew', '自动续签'), - dataIndex: 'autoRenew', + dataIndex: 'auto_renew', valueType: 'switch', - + render: (_, record) => { + return + } }, { title: t('website.ssl.columns.pushDir', '推送证书到本地目录'), - dataIndex: 'pushDir', + dataIndex: 'push_dir', valueType: 'switch', hideInTable: true, hideInSearch: true, }, { - name: [ 'pushDir' ], + name: [ 'push_dir' ], valueType: 'dependency', hideInSetting: true, + hideInTable: true, columns: ({ pushDir }) => { if (pushDir) { return [ { @@ -187,6 +194,11 @@ const SSL = () => { dataIndex: 'description', }, { + title: t('website.ssl.columns.expire_date', '过期时间'), + dataIndex: 'expire_date', + valueType: 'dateTime' + }, + { title: t('website.ssl.columns.option', '操作'), valueType: 'option', key: 'option', render: (_, record) => [ diff --git a/src/service/websites.ts b/src/service/websites.ts index 0191bd7..90848ef 100644 --- a/src/service/websites.ts +++ b/src/service/websites.ts @@ -1,18 +1,23 @@ import { createCURD } from '@/service/base.ts' import { WebSite } from '@/types' +import request from '@/request.ts' const websitesServ = { ssl: { ...createCURD('/website/ssl') }, - acme:{ + acme: { ...createCURD('/website/acme') }, - dns:{ - ...createCURD('/website/dns') + dns: { + ...createCURD('/website/dns_account') }, - ca:{ - ...createCURD('/website/ca') + ca: { + ...createCURD('/website/ca'), + obtainSsl: async (params: WebSite.ISSLObtainByCA) => { + return request.post('/website/ca/obtain_ssl', params) + }, + } } diff --git a/src/store/websites/acme.ts b/src/store/websites/acme.ts index 676724a..9ccf02a 100644 --- a/src/store/websites/acme.ts +++ b/src/store/websites/acme.ts @@ -7,13 +7,13 @@ import { atom } from 'jotai' import { WebSite } from '@/types' export enum AcmeType { - LetsEncrypt = 'letsencrypt', + LetsEncrypt = 'LetsEncrypt', //zerossl - ZeroSSl = 'zerossl', + ZeroSSl = 'ZeroSSl', //buypass - Buypass = 'buypass', + Buypass = 'Buypass', //google - Google = 'google', + Google = 'Google', } export const AcmeAccountTypes = [ diff --git a/src/store/websites/ca.ts b/src/store/websites/ca.ts index e8c0898..c91c6ea 100644 --- a/src/store/websites/ca.ts +++ b/src/store/websites/ca.ts @@ -5,6 +5,8 @@ import websitesServ from '@/service/websites.ts' import { message, } from 'antd' import { t } from 'i18next' import { WebSite } from '@/types' +import { ISSLObtainByCA } from '@/types/website/ca' +import { sslListAtom } from '@/store/websites/ssl.ts' export const caPageAtom = atom({ page: 1, pageSize: 10, @@ -52,4 +54,17 @@ export const deleteCaAtom = atomWithMutation(get => ({ message.success(t('message.deleteSuccess', '删除成功')) get(caListAtom).refetch() } +})) + + +export const obtainSslAtom = atomWithMutation(get => ({ + mutationKey: [ 'obtainSsl' ], + mutationFn: async (data) => { + return await websitesServ.ca.obtainSsl(data) + }, + onSuccess: (res) => { + message.success(t('message.obtainSsl', '签发成功')) + get(sslListAtom).refetch() + return res + } })) \ No newline at end of file diff --git a/src/store/websites/ssl.ts b/src/store/websites/ssl.ts index 017923d..a020f73 100644 --- a/src/store/websites/ssl.ts +++ b/src/store/websites/ssl.ts @@ -12,6 +12,22 @@ export enum ProviderTypeEnum { Http = 'http' } + +export function getProvider(provider: string): string { + switch (provider) { + case 'dnsAccount': + return t('website.dnsAccount', 'DNS账号') + case 'dnsManual': + return t('website.dnsManual', '手动解析') + case 'http': + return 'HTTP' + case 'selfSigned': + return t('website.ssl.selfSigned', '自签证书') + default: + return t('website.ssl.manualCreate', '手动创建') + } +} + export enum KeyTypeEnum { EC256 = 'P256', EC384 = 'P384', @@ -26,8 +42,11 @@ export const KeyTypes = [ { label: 'RSA 2048', value: '2048' }, { label: 'RSA 3072', value: '3072' }, { label: 'RSA 4096', value: '4096' }, -]; +] +export const getKeyType = (key: string): string => { + return KeyTypes.find(item => item.value === key)?.label || '' +} export const sslPageAtom = atom({ page: 1, diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 0272155..b7e8cf3 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -8,7 +8,7 @@ export namespace System { export namespace WebSite { export { IAcmeAccount } from './website/acme' - export { ICA } from './website/ca' + export { ICA, ISSLObtainByCA } from './website/ca' export { IDnsAccount } from './website/dns' export { ISSL, ProviderType, SSLSearchParam } from './website/ssl' } \ No newline at end of file diff --git a/src/types/website/ca.d.ts b/src/types/website/ca.d.ts index fab3f47..b33148c 100644 --- a/src/types/website/ca.d.ts +++ b/src/types/website/ca.d.ts @@ -14,4 +14,17 @@ export interface ICA { city: string; private_key: string; key_type: string; +} + + +export interface ISSLObtainByCA { + id: number; + domains: string; + key_type: string; + time: number; + unit: string; + push_dir: boolean; + dir: string; + auto_renew: boolean; + description: string; } \ No newline at end of file