Browse Source

增加邮箱登录

main
lk 2 weeks ago
parent
commit
80f33e4cf6
  1. 590
      src/pages/websites/cert/management.tsx

590
src/pages/websites/cert/management.tsx

@ -0,0 +1,590 @@
import React, { useEffect, useMemo, useState } from "react";
import {
Table,
Input,
Button,
Tag,
Progress,
Space,
Drawer,
Typography,
Form,
DatePicker,
Tooltip,
Modal,
ProgressProps,
} from "antd";
import { CheckCircleFilled, CheckCircleOutlined, CopyOutlined, SearchOutlined } from "@ant-design/icons";
import { t } from "@/i18n.ts";
import ListPageLayout from "@/layout/ListPageLayout.tsx";
import TextArea from "antd/es/input/TextArea";
import { useNavigate } from "@tanstack/react-router";
import {
certListAtom,
checkDomainAtom,
deletesCertificateAtom,
downloadCertificateAtom,
editCertificateAtom,
getCertificateLogsAtom,
Req_CertList,
req_CertLogs,
Req_DeletesCert,
Req_DownloadCert,
Req_UpdateCert,
saveOrUpdateCertAtom,
} from "@/store/websites/cert.ts";
import { useAtomValue } from "jotai/index";
import { format } from "date-fns";
import websitesServ from "@/service/websites.ts";
import { useAtom } from "jotai";
import { getToken } from "@/store/system.ts";
const { confirm } = Modal;
const { Text, Link } = Typography;
const i18nPrefix = "cert.management";
const CertApplyingDrawer = (props: { id: number; onCloseApplyingDrawer; applyingDrawerVisible }) => {
const [dData, setDdata] = useState<req_CertLogs>({
log_id: props.id,
log_type: "cert_apply",
websocket: false,
log_pos: 0,
// 根据实际数据结构添加其他属性的初始值
});
const [showTextInfo, setShowTextInfo] = useState("");
const [onChangeDT, setOnChangeDT] = useState(0);
const { data: certLogsData, isFetching: certLogsFetching } = useAtomValue(
useMemo(() => getCertificateLogsAtom(dData), [onChangeDT]),
);
useEffect(() => {
if (certLogsData && (certLogsData as any)?.log) {
setShowTextInfo((prev) => prev + (certLogsData as any).log.toString());
}
}, [certLogsData]);
useEffect(() => {
if (props.applyingDrawerVisible) {
setShowTextInfo("");
setOnChangeDT((prev) => prev + 1);
}
let timer: number | undefined;
if (props.applyingDrawerVisible) {
timer = window.setInterval(() => {
setDdata((prevState) => ({
...prevState,
log_id: props.id,
log_pos: (certLogsData as any)?.log_pos || 0,
}));
// console.log("update drawer");
setOnChangeDT((prev) => prev + 1);
}, 5000);
} else {
setDdata((prevState) => ({
...prevState,
log_id: props.id,
log_pos: 0,
}));
window.clearInterval(timer);
}
return () => window.clearInterval(timer);
}, [props.applyingDrawerVisible]);
return (
<Drawer
title="证书申请中..."
width={window.innerWidth / 2}
placement="right"
onClose={props.onCloseApplyingDrawer}
visible={props.applyingDrawerVisible}
bodyStyle={{ display: "flex", flexDirection: "column", height: "100%" }}
>
<TextArea value={showTextInfo} readOnly style={{ flex: 1 }} />
</Drawer>
);
};
const CertificateManagement: React.FC = () => {
const [dlDrawerVisible, setDLDrawerVisible] = useState(false);
const [editDrawerVisible, setEditDrawerVisible] = useState(false);
const [detailsVisible, setDetailsVisible] = useState(false);
const navigate = useNavigate();
const dData: Req_CertList = { order: "", prop: "", page: 1, pageSize: 10 };
const [updataList, setUpdataList] = useState(0);
const { data: certListData, isFetching: certListFetching } = useAtomValue(
useMemo(() => certListAtom(dData), [updataList]),
);
const [applyingDrawerVisible, setApplyingDrawerVisible] = useState(false);
const [certApplyingId, setCertApplyingId] = useState(0);
const [currentDLInfo, setCurrentDLInfo] = useState({});
const { mutate: editCertUpdate } = useAtomValue(editCertificateAtom);
const [remakeTextInfo, setRemakeTextInfo] = useState("");
useEffect(() => {
window.setInterval(() => {
// console.log("update drawer");
setUpdataList((prev) => prev + 1);
}, 5000);
}, []);
const showDLDrawer = (record) => {
setCurrentDLInfo(record);
setDLDrawerVisible(true);
};
const closeDLDrawer = () => {
setDLDrawerVisible(false);
};
const showEditDrawer = (record) => {
setCurrentDLInfo(record);
setEditDrawerVisible(true);
};
const enterEditDrawer = (record) => {
const data: Req_UpdateCert = {
id: record.id,
remark: remakeTextInfo,
};
editCertUpdate(data);
setEditDrawerVisible(false);
};
const closeEditDrawer = () => {
setEditDrawerVisible(false);
};
const openDetails = () => {
setDetailsVisible(true);
};
const closeDetails = () => {
setDetailsVisible(false);
};
const showApplyingDrawer = () => {
setApplyingDrawerVisible(true);
};
const onCloseApplyingDrawer = () => {
setApplyingDrawerVisible(false);
};
const applyCertClick = () => {
navigate({ to: `/client/cert/apply` }); // 确保路径正确
};
const clickStateTag = (id: number) => {
setCertApplyingId(id);
showApplyingDrawer();
};
const handleRemarkChange = (e) => {
setRemakeTextInfo(e.target.value);
};
const clickDownLoad = (record: any) => {
const cert_format: string = record.certificateFormat.toString().split("/")[0].replace(/\s+/g, "");
// const data: Req_DownloadCert = {
// id: (currentDLInfo as any)?.id,
// cert_format: cert_format,
// };
// websitesServ.cert.downloadCertificate(data);
// eslint-disable-next-line react-hooks/rules-of-hooks
const token = getToken();
const certUrl = "http://127.0.0.1:8000/api/v1/cert";
const url = `${certUrl}/apply/download?id=${(currentDLInfo as any)?.id}&cert_format=${cert_format}&token=${token}`;
window.open(url, "_blank");
};
const clickDelete = (id: number) => {
confirm({
title: "提示",
content: "是否要删除证书?",
okText: "确定",
okType: "danger",
cancelText: "取消",
onOk() {
const data: Req_DeletesCert = { ids: [id] };
websitesServ.cert.deletesCertificate(data);
},
onCancel() {
console.log("取消删除证书");
},
});
};
const drawerColumns = [
{
title: "服务器类型",
dataIndex: "serverType",
key: "serverType",
},
{
title: "证书格式",
dataIndex: "certificateFormat",
key: "certificateFormat",
render: (text: any) => (
<Space>
{text.split("/").map((format: string) => (
<Button key={format} size="small" icon={<CopyOutlined />} iconPosition={"end"}>
{format.trim()}
</Button>
))}
</Space>
),
},
{
title: "操作",
key: "canDownLoad",
render: (text: string, record: any) => (
<Space size="middle">
<Button type="link"></Button>
<Button type="link" disabled={!record.canDownLoad} onClick={() => clickDownLoad(record)}>
</Button>
</Space>
),
},
];
const drawerDataSource = [
{
key: "1",
serverType: "Nginx",
certificateFormat: "pem / key",
canDownLoad: true,
},
{
key: "2",
serverType: "Tomcat",
certificateFormat: "pfx",
canDownLoad: false,
},
{
key: "3",
serverType: "Apache",
certificateFormat: "crt / key",
canDownLoad: true,
},
{
key: "4",
serverType: "IIS",
certificateFormat: "pfx",
canDownLoad: false,
},
{
key: "5",
serverType: "JKS",
certificateFormat: "jks",
canDownLoad: true,
},
{
key: "6",
serverType: "宝塔",
certificateFormat: "pem / key",
canDownLoad: true,
},
{
key: "7",
serverType: "其他",
certificateFormat: "pem / key",
canDownLoad: true,
},
];
const columns = [
{
title: "域名",
dataIndex: "primary_domain",
key: "primary_domain",
render: (text: string) => (
<Tooltip title={`点击查看详情`} color={"orange"}>
<Button type="link" onClick={openDetails}>
{text}
</Button>
</Tooltip>
),
},
{
title: "域名验证",
dataIndex: "domainVerification",
key: "domainVerification",
render: () => <CheckCircleFilled style={{ color: "green" }} />,
},
{
title: "证书品牌",
dataIndex: "acme_type",
key: "acme_type",
render: (text) => (
<Tooltip title={`证书名字`}>
<span>{text}</span>
{/*<img src="https://placehold.co/20x20?text=Logo" alt="Let's Encrypt Logo" />{" "}*/}
</Tooltip>
),
},
{
/*
const (
SSLSuccess CertStatus = "success" // 成功
SSLInit CertStatus = "init" // 初始化
SSLError CertStatus = "error" // 错误
SSLReady CertStatus = "ready" // 准备中
SSLApply CertStatus = "applying" // 申请中
SSLApplyError CertStatus = "applyError" // 申请失败
)
*/
title: "有效期(天)",
dataIndex: "validityDays",
key: "validityDays",
render: (text, record: { created_at: string; updated_at: string; expiredate: string }) => {
if (record) {
// const twoColors: ProgressProps["strokeColor"] = {
// "0%": "#00ff59",
// "100%": "#ff0000",
// };
record.created_at = !record?.created_at ? format(new Date(), "yyyy-MM-dd HH:mm:ss") : record.created_at;
record.expiredate = !record?.expiredate ? format(new Date(), "yyyy-MM-dd HH:mm:ss") : record.expiredate;
const createdDate = new Date(record.created_at.replace(/-/g, "/"));
const expireDateObj = new Date(record.expiredate.replace(/-/g, "/"));
const currentDate = new Date();
const totalValidTime = expireDateObj.getTime() - createdDate.getTime();
const remainingTime = expireDateObj.getTime() - currentDate.getTime();
const percentage = (remainingTime / totalValidTime) * 100;
//const percentage = 60;
let cColor = "#000000";
if (percentage < 50) {
cColor = "#00ff59";
} else if (percentage >= 50 && percentage <= 75) {
cColor = "#00641a";
} else if (percentage >= 75 && percentage <= 90) {
cColor = "#ffdd00";
} else {
cColor = "#ff0000";
}
return (
<Space>
<Tooltip
title={
<div>
{record.created_at}
<br />
{record.expiredate}
</div>
}
>
<Progress
style={{ width: 200 }}
percent={percentage}
strokeColor={cColor}
size="small"
showInfo={false}
/>
<span>{Math.round(percentage)}/100</span>
</Tooltip>
</Space>
);
}
},
},
{
title: "加密方式",
dataIndex: "keytype",
key: "keytype",
render: (encryptionMethod: string) => <Tag color="green">{encryptionMethod}</Tag>,
},
{
title: "状态",
dataIndex: "cert_status",
key: "cert_status",
render: (state, record: any) => {
const { id } = record;
if (state === "applying") {
return (
<Tag color="blue" onClick={() => clickStateTag(id)} style={{ cursor: "pointer" }}>
{state}
</Tag>
);
} else {
return (
<Tag color="green" onClick={() => clickStateTag(id)} style={{ cursor: "pointer" }}>
{state}
</Tag>
);
}
},
},
{
title: "备注",
dataIndex: "remark",
key: "remark",
},
{
title: "操作",
key: "action",
render: (record: any) => (
<Space size="middle">
<a onClick={() => showEditDrawer(record)}></a>
<a onClick={() => showDLDrawer(record)}></a>
<a onClick={() => clickDelete(record.id)}></a>
</Space>
),
},
];
const detailsColumns = [
{
title: "状态",
dataIndex: "status",
key: "status",
render: (text: string) => <span style={{ color: "#52c41a" }}>{text}</span>,
},
{
title: "域名",
dataIndex: "domain",
key: "domain",
},
{
title: "服务商",
dataIndex: "provider",
key: "provider",
render: (text: string) => <span style={{ color: "#1890ff" }}>{text}</span>,
},
{
title: "主机记录",
dataIndex: "hostRecord",
key: "hostRecord",
},
{
title: "记录类型",
dataIndex: "recordType",
key: "recordType",
render: (text: string) => <span style={{ color: "red" }}>{text}</span>,
},
{
title: "记录值",
dataIndex: "recordValue",
key: "recordValue",
},
{
title: "操作",
dataIndex: "action",
key: "action",
render: (text: string) => <Link href="#">{text}</Link>,
},
];
const detailsData = [
{
key: "1",
status: "通过",
domain: "*.bidiy2.com",
provider: "Cloudflare",
hostRecord: "_acme-challenge",
recordType: "CNAME",
recordValue: "8cb0d498cecabd92.httpsok.com",
action: "检测命令",
},
];
return (
<div>
<ListPageLayout title={t(`${i18nPrefix}.apply.title`, "证书申请")}>
<Space style={{ marginBottom: 16 }}>
<Input addonBefore={<SearchOutlined />} placeholder="域名 | 备注" style={{ width: 200 }} />
<Button></Button>
<Button type="primary" onClick={applyCertClick}>
</Button>
</Space>
<Table columns={columns} dataSource={(certListData as any)?.rows} pagination={false} />
</ListPageLayout>
<CertApplyingDrawer
id={certApplyingId}
onCloseApplyingDrawer={onCloseApplyingDrawer}
applyingDrawerVisible={applyingDrawerVisible}
/>
<Drawer title="下载证书" placement="right" onClose={closeDLDrawer} visible={dlDrawerVisible} width={600}>
<Text>:</Text>
<Table dataSource={drawerDataSource} columns={drawerColumns} pagination={false} style={{ marginTop: 16 }} />
<div style={{ backgroundColor: "#f6ffed", padding: 16, marginTop: 16 }}>
<Text>SSL证书交流群</Text>
<br />
<Text> </Text>
<br />
<Link href="https://httpsok"> httpsok</Link>
</div>
</Drawer>
<Drawer
title="编辑"
placement="right"
closable={false}
onClose={closeEditDrawer}
visible={editDrawerVisible}
style={{ textAlign: "center" }}
bodyStyle={{ display: "flex", justifyContent: "center", alignItems: "center" }}
>
<Form layout="vertical" style={{ maxWidth: "400px", width: "100%" }}>
<Form.Item label="域名">
<span>{(currentDLInfo as any)?.primary_domain}</span>
</Form.Item>
<Form.Item label="证书品牌">
<span>{(currentDLInfo as any)?.acme_type}</span>
</Form.Item>
<Form.Item label="失效时间">
<span>{(currentDLInfo as any)?.expiredate}</span>
</Form.Item>
<Form.Item label="创建时间">
<span>{(currentDLInfo as any)?.created_at}</span>
</Form.Item>
<Form.Item label="备注">
<TextArea
rows={4}
maxLength={100}
defaultValue={(currentDLInfo as any)?.remark}
onChange={handleRemarkChange}
/>
</Form.Item>
<Form.Item>
<Button onClick={closeEditDrawer} style={{ marginRight: 8 }}>
</Button>
<Button type="primary" onClick={() => enterEditDrawer(currentDLInfo)}>
</Button>
</Form.Item>
</Form>
</Drawer>
<Modal
title="域名验证"
visible={detailsVisible}
onOk={openDetails}
onCancel={closeDetails}
width={1200} // 设置宽度为800px
footer={[
<Button key="ok" type="primary" onClick={openDetails}>
</Button>,
]}
>
<Text>
DNS解析记录 <Link href="#"></Link>
</Text>
<br />
<Text>1. </Text>
<br />
<Text>
2. 1-2(<Text type="danger"></Text>)
</Text>
<Table columns={detailsColumns} dataSource={detailsData} pagination={false} />
</Modal>
</div>
);
};
export default CertificateManagement;
Loading…
Cancel
Save