diff --git a/src/pages/websites/ssl/components/AcmeList.tsx b/src/pages/websites/ssl/components/AcmeList.tsx
index 9575329..fc99e81 100644
--- a/src/pages/websites/ssl/components/AcmeList.tsx
+++ b/src/pages/websites/ssl/components/AcmeList.tsx
@@ -2,10 +2,10 @@ import { useMemo, useState } from 'react'
import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components'
import { IAcmeAccount } from '@/types/website/acme'
import { useTranslation } from '@/i18n.ts'
-import { acmeListAtom, acmePageAtom, AcmeType, saveOrUpdateAcmeAtom } from '@/store/websites/acme.ts'
+import { AcmeAccountTypes, acmeListAtom, acmePageAtom, AcmeType, saveOrUpdateAcmeAtom } from '@/store/websites/acme.ts'
import { useAtom, useAtomValue } from 'jotai'
import { Alert, Button, Form } from 'antd'
-import { KeyTypeEnum } from '@/store/websites/ssl.ts'
+import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts'
const AcmeList = () => {
@@ -41,12 +41,7 @@ const AcmeList = () => {
dataIndex: 'type',
valueType: 'select',
fieldProps: {
- options: [
- { label: 'Let\'s Encrypt', value: AcmeType.LetsEncrypt },
- { label: 'ZeroSSl', value: AcmeType.ZeroSSl },
- { label: 'Buypass', value: AcmeType.Buypass },
- { label: 'Google Cloud', value: AcmeType.Google },
- ]
+ options: AcmeAccountTypes
},
formItemProps: {
rules: [
@@ -58,15 +53,8 @@ const AcmeList = () => {
title: t('website.ssl.acme.columns.keyType', '密钥算法'),
dataIndex: 'keyType',
valueType: 'select',
- initialValue: KeyTypeEnum.EC256,
fieldProps: {
- options: [
- { label: t('website.ssl.keyTypeEnum.EC256', 'EC 256'), value: KeyTypeEnum.EC256 },
- { label: t('website.ssl.keyTypeEnum.EC384', 'EC 384'), value: KeyTypeEnum.EC384 },
- { label: t('website.ssl.keyTypeEnum.RSA2048', 'RSA 2048'), value: KeyTypeEnum.RSA2048 },
- { label: t('website.ssl.keyTypeEnum.RSA3072', 'RSA 3072'), value: KeyTypeEnum.RSA3072 },
- { label: t('website.ssl.keyTypeEnum.RSA4096', 'RSA 4096'), value: KeyTypeEnum.RSA4096 },
- ]
+ options: KeyTypes
},
formItemProps: {
rules: [
@@ -85,8 +73,6 @@ const AcmeList = () => {
valueType: 'option',
render: (_, record) => {
return [
- {
- }}>{t('actions.edit', '编辑')},
{
}}>{t('actions.delete', '删除')},
]
diff --git a/src/pages/websites/ssl/components/CAList.tsx b/src/pages/websites/ssl/components/CAList.tsx
new file mode 100644
index 0000000..bf39c0e
--- /dev/null
+++ b/src/pages/websites/ssl/components/CAList.tsx
@@ -0,0 +1,150 @@
+import { useMemo, useState } from 'react'
+import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components'
+import { ICA, IcaAccount } from '@/types/website/ca'
+import { useTranslation } from '@/i18n.ts'
+import { caAccountTypes, caType } from '@/store/websites/ca.ts'
+import { useAtom, useAtomValue } from 'jotai'
+import { Alert, Button, Form } from 'antd'
+import { KeyTypeEnum, KeyTypes } from '@/store/websites/ssl.ts'
+import { caListAtom, caPageAtom, saveOrUpdateCaAtom } from '@/store/websites/ca.ts'
+
+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 [ 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.columns.keyType', '密钥算法'),
+ dataIndex: 'keyType',
+ valueType: 'select',
+ fieldProps: {
+ options: KeyTypes
+ },
+ formItemProps: {
+ rules: [
+ { required: true, message: t('message.required', '请选择') }
+ ]
+ },
+ },
+ {
+ title: t('website.ssl.ca.columns.url', 'URL'),
+ dataIndex: 'url',
+ valueType: 'text',
+ ellipsis: true, // 文本溢出省略
+ hideInForm: true,
+ }, {
+ title: '操作',
+ valueType: 'option',
+ render: (_, record) => {
+ return [
+ {
+ }}>{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/index.tsx b/src/pages/websites/ssl/index.tsx
index 538492e..58622f2 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 {
- KeyTypeEnum,
+ KeyTypes,
ProviderTypeEnum,
saveOrUpdateSslAtom,
sslListAtom,
@@ -16,6 +16,8 @@ import { PlusOutlined } from '@ant-design/icons'
import { ISSL } from '@/types/website/ssl'
import DrawerPicker from '@/components/drawer-picker/DrawerPicker.tsx'
import AcmeList from '@/pages/websites/ssl/components/AcmeList.tsx'
+import { acmeListAtom, AcmeType, getAcmeAccountTypeName } from '@/store/websites/acme.ts'
+import { dnsListAtom, getDNSTypeName } from '@/store/websites/dns.ts'
const SSL = () => {
@@ -24,6 +26,8 @@ const SSL = () => {
const [ form ] = Form.useForm()
const [ page, setPage ] = useAtom(sslPageAtom)
const [ search, setSearch ] = useAtom(sslSearchAtom)
+ const { data: acmeData, isLoading: acmeLoading } = useAtomValue(acmeListAtom)
+ const { data: dnsData, isLoading: dnsLoading } = useAtomValue(dnsListAtom)
const { data, isLoading, isFetching, refetch } = useAtomValue(sslListAtom)
const { mutate: saveOrUpdate, isSuccess, isPending: isSubmitting } = useAtomValue(saveOrUpdateSslAtom)
@@ -55,21 +59,27 @@ const SSL = () => {
{
title: t('website.ssl.columns.acmeAccountId', 'Acme帐号'),
dataIndex: 'acmeAccountId',
+ valueType: 'select',
+ fieldProps: {
+ loading: acmeLoading,
+ options: acmeData?.rows?.map(item => ({
+ label: `${item.email} [${getAcmeAccountTypeName(item.type as AcmeType)}]`,
+ value: item.id
+ }))
+ },
+ formItemProps: {
+ rules: [
+ { required: true, message: t('message.required', '请选择') }
+ ]
+ }
},
{
title: t('website.ssl.columns.keyType', '密钥算法'),
dataIndex: 'keyType',
hideInTable: true,
valueType: 'select',
- initialValue: KeyTypeEnum.EC256,
fieldProps: {
- options: [
- { label: t('website.ssl.keyTypeEnum.EC256', 'EC 256'), value: KeyTypeEnum.EC256 },
- { label: t('website.ssl.keyTypeEnum.EC384', 'EC 384'), value: KeyTypeEnum.EC384 },
- { label: t('website.ssl.keyTypeEnum.RSA2048', 'RSA 2048'), value: KeyTypeEnum.RSA2048 },
- { label: t('website.ssl.keyTypeEnum.RSA3072', 'RSA 3072'), value: KeyTypeEnum.RSA3072 },
- { label: t('website.ssl.keyTypeEnum.RSA4096', 'RSA 4096'), value: KeyTypeEnum.RSA4096 },
- ]
+ options: KeyTypes
},
formItemProps: {
rules: [
@@ -116,9 +126,18 @@ const SSL = () => {
return [ {
title: t('website.ssl.columns.dnsAccountId', 'DNS帐号'),
dataIndex: 'dnsAccountId',
+ valueType: 'select',
formItemProps: {
rules: [ { required: true, message: t('message.required', '请输入DNS帐号') } ]
- }
+ },
+ fieldProps: {
+ loading: dnsLoading,
+ options: dnsData?.rows.map(item => ({
+ label: `${item.name} [${getDNSTypeName(item.type)}]`,
+ value: item.id
+ }))
+ },
+
} ]
}
@@ -224,7 +243,7 @@ const SSL = () => {
,
- {t( 'website.ssl.actions.dns', 'DNS帐户')}
+ {t('website.ssl.actions.dns', 'DNS帐户')}
}>
,
@@ -240,7 +259,7 @@ const SSL = () => {
}}
type="primary"
>
- {t('actions.add', '申请证书')}
+ {t('actions.sslApply', '申请证书')}
,
]
}}
diff --git a/src/service/websites.ts b/src/service/websites.ts
index 2e7546a..f3cf596 100644
--- a/src/service/websites.ts
+++ b/src/service/websites.ts
@@ -1,6 +1,8 @@
import { createCURD } from '@/service/base.ts'
import { ISSL } from '@/types/website/ssl'
import { IAcmeAccount } from '@/types/website/acme'
+import { IDnsAccount } from '@/types/website/dns'
+import { ICA } from '@/types/website/ca'
const websitesServ = {
ssl: {
@@ -8,7 +10,14 @@ const websitesServ = {
},
acme:{
...createCURD('/website/acme')
+ },
+ dns:{
+ ...createCURD('/website/dns')
+ },
+ ca:{
+ ...createCURD('/website/ca')
}
+
}
export default websitesServ
\ No newline at end of file
diff --git a/src/store/websites/acme.ts b/src/store/websites/acme.ts
index 88e4bb5..f163984 100644
--- a/src/store/websites/acme.ts
+++ b/src/store/websites/acme.ts
@@ -3,7 +3,7 @@ import { IAcmeAccount } from '@/types/website/acme'
import websitesServ from '@/service/websites.ts'
import { message } from 'antd'
import { t } from 'i18next'
-import { IPage } from '@/global'
+import { IApiResult, IPage } from '@/global'
import { atom } from 'jotai'
export enum AcmeType {
@@ -16,6 +16,17 @@ export enum AcmeType {
Google = 'google',
}
+export const AcmeAccountTypes = [
+ { label: 'Let\'s Encrypt', value: AcmeType.LetsEncrypt },
+ { label: 'ZeroSSL', value: AcmeType.ZeroSSl },
+ { label: 'Buypass', value: AcmeType.Buypass },
+ { label: 'Google Cloud', value: AcmeType.Google },
+]
+
+export const getAcmeAccountTypeName = (type: AcmeType) => {
+ return AcmeAccountTypes.find(item => item.value === type)?.label
+}
+
export const acmePageAtom = atom({
page: 1, pageSize: 10,
})
@@ -47,3 +58,14 @@ export const saveOrUpdateAcmeAtom = atomWithMutation(get => (
return res
}
}))
+
+export const deleteAcmeAtom = atomWithMutation(get => ({
+ mutationKey: [ 'sslDelete' ],
+ mutationFn: async (id) => {
+ return await websitesServ.acme.delete(id)
+ },
+ onSuccess: () => {
+ message.success(t('message.deleteSuccess', '删除成功'))
+ get(acmeListAtom).refetch()
+ }
+}))
\ No newline at end of file
diff --git a/src/store/websites/ca.ts b/src/store/websites/ca.ts
index e69de29..0b8c1be 100644
--- a/src/store/websites/ca.ts
+++ b/src/store/websites/ca.ts
@@ -0,0 +1,50 @@
+import { atom } from 'jotai/index'
+import { IApiResult, IPage } from '@/global'
+import { atomWithMutation, atomWithQuery } from 'jotai-tanstack-query'
+import websitesServ from '@/service/websites.ts'
+import { message } from 'antd'
+import { t } from 'i18next'
+import { ICA } from '@/types/website/ca'
+
+export const caPageAtom = atom({
+ page: 1, pageSize: 10,
+})
+
+//list
+export const caListAtom = atomWithQuery(get => ({
+ queryKey: [ 'caList', get(caPageAtom) ],
+ queryFn: async ({ queryKey: [ , page ] }) => {
+ return await websitesServ.ca.list(page)
+ },
+ select: (data) => {
+ return data.data
+ }
+}))
+
+//saveOrUpdate
+export const saveOrUpdateCaAtom = atomWithMutation(get => ({
+ mutationKey: [ 'saveOrUpdateCA' ],
+ mutationFn: async (data: ICA) => {
+ if (data.id > 0) {
+ return await websitesServ.ca.update(data)
+ }
+ return await websitesServ.ca.add(data)
+ },
+ onSuccess: (res) => {
+ const isAdd = !!res.data?.id
+ message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
+ get(caListAtom).refetch()
+ return res
+ }
+}))
+
+export const deleteCaAtom = atomWithMutation(get => ({
+ mutationKey: [ 'sslDelete' ],
+ mutationFn: async (id) => {
+ return await websitesServ.ca.delete(id)
+ },
+ onSuccess: () => {
+ message.success(t('message.deleteSuccess', '删除成功'))
+ get(caListAtom).refetch()
+ }
+}))
\ No newline at end of file
diff --git a/src/store/websites/dns.ts b/src/store/websites/dns.ts
index 36a8c9d..e75d322 100644
--- a/src/store/websites/dns.ts
+++ b/src/store/websites/dns.ts
@@ -1,10 +1,79 @@
-export interface IDnsAccount {
- id: number;
- createdAt: Date | null;
- createdBy: number;
- updatedAt: Date | null;
- updatedBy: number;
- name: string;
- type: string;
- authorization: string;
-}
\ No newline at end of file
+import { t } from 'i18next'
+import { atom } from 'jotai/index'
+import { IPage } from '@/global'
+import { atomWithMutation, atomWithQuery } from 'jotai-tanstack-query'
+import websitesServ from '@/service/websites.ts'
+import { message } from 'antd'
+import { IDnsAccount } from '@/types/website/dns'
+
+export const DNSTypes = [
+ {
+ label: t('website.dns.types.aliyun', '阿里云DNS'),
+ value: 'AliYun',
+ },
+ {
+ label: t('website.dns.types.tencentCloud', '腾讯云'),
+ value: 'TencentCloud',
+ },
+ {
+ label: t('website.dns.types.dnsPod', 'DNSPod'),
+ value: 'DnsPod',
+ },
+ {
+ label: 'CloudFlare',
+ value: 'CloudFlare',
+ },
+ {
+ label: 'NameSilo',
+ value: 'NameSilo',
+ },
+ {
+ label: 'NameCheap',
+ value: 'NameCheap',
+ },
+ {
+ label: 'Name.com',
+ value: 'NameCom',
+ },
+ {
+ label: 'GoDaddy',
+ value: 'Godaddy',
+ },
+]
+
+
+export const getDNSTypeName = (type: string) => {
+ return DNSTypes.find(item => item.value === type)?.label
+}
+
+export const dnsPageAtom = atom({
+ page: 1, pageSize: 10,
+})
+
+//list
+export const dnsListAtom = atomWithQuery(get => ({
+ queryKey: [ 'dnsList', get(dnsPageAtom) ],
+ queryFn: async ({ queryKey: [ , page ] }) => {
+ return await websitesServ.dns.list(page)
+ },
+ select: (data) => {
+ return data.data
+ }
+}))
+
+//saveOrUpdate
+export const saveOrUpdateDNSAtom = atomWithMutation(get => ({
+ mutationKey: [ 'saveOrUpdatedDNS' ],
+ mutationFn: async (data: IDnsAccount) => {
+ if (data.id > 0) {
+ return await websitesServ.dns.update(data)
+ }
+ return await websitesServ.dns.add(data)
+ },
+ onSuccess: (res) => {
+ const isAdd = !!res.data?.id
+ message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
+ get(dnsListAtom).refetch()
+ return res
+ }
+}))
diff --git a/src/store/websites/ssl.ts b/src/store/websites/ssl.ts
index e4c7804..c3b1297 100644
--- a/src/store/websites/ssl.ts
+++ b/src/store/websites/ssl.ts
@@ -20,6 +20,15 @@ export enum KeyTypeEnum {
RSA4096 = '4096',
}
+export const KeyTypes = [
+ { label: 'EC 256', value: 'P256' },
+ { label: 'EC 384', value: 'P384' },
+ { label: 'RSA 2048', value: '2048' },
+ { label: 'RSA 3072', value: '3072' },
+ { label: 'RSA 4096', value: '4096' },
+];
+
+
export const sslPageAtom = atom({
page: 1,
pageSize: 20,