mirror of https://github.com/certd/certd
				
				
				
			perf: 证书到期剩余天数进度条根据实际证书有效期计算 (#528) nicheng-he
* Create FUNDING.yml * Update FUNDING.yml * Update README.md * Update README.md * Update README.md * Update README.md * Update README_en.md * 证书到期剩余天数进度条根据实际证书时间计算 --------- Co-authored-by: greper <xiaojunnuo@qq.com>pull/539/head
							parent
							
								
									1476b9cb9c
								
							
						
					
					
						commit
						2d4586b1c4
					
				|  | @ -0,0 +1,5 @@ | |||
| # These are supported funding model platforms | ||||
| 
 | ||||
| github: greper | ||||
| buy_me_a_coffee: greper | ||||
| custom: ['https://afdian.com/a/greper'] | ||||
|  | @ -152,7 +152,7 @@ https://certd.handfree.work/ | |||
| 
 | ||||
| ## 八、捐赠 | ||||
| ************************ | ||||
| 支持开源,为爱发电,我已入驻爱发电    | ||||
| 支持开源,为爱发电,我已入驻爱发电 | ||||
| https://afdian.com/a/greper | ||||
| 
 | ||||
| 发电权益: | ||||
|  | @ -171,6 +171,7 @@ https://afdian.com/a/greper | |||
| | 自动部署插件  | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖                             | | ||||
| | 通知      | 邮件通知、自定义webhook                       | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 | | ||||
| 
 | ||||
| ************************ | ||||
| 
 | ||||
| ************************ | ||||
| 
 | ||||
|  |  | |||
|  | @ -134,6 +134,8 @@ You can also add the author as a friend. | |||
| | QR Code | <img height="230" src="./docs/guide/contact/images/me.png"> | | ||||
| 
 | ||||
| ## 8. Donation | ||||
| ************************ | ||||
|  [](https://github.com/sponsors/greper) | ||||
| ************************ | ||||
| Support open-source projects and contribute with love. I've joined Afdian. | ||||
| https://afdian.com/a/greper | ||||
|  |  | |||
|  | @ -99,6 +99,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin { | |||
|     const cert: CertInfo = certReader.toCertInfo(); | ||||
|     this.cert = cert; | ||||
| 
 | ||||
|     this._result.pipelineVars.certEffectiveTime = dayjs(certReader.detail.notBefore).valueOf(); | ||||
|     this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf(); | ||||
|     if (!this._result.pipelinePrivateVars) { | ||||
|       this._result.pipelinePrivateVars = {}; | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ export class CertReader { | |||
| 
 | ||||
|   detail: CertificateInfo; | ||||
|   //毫秒时间戳
 | ||||
|   effective: number; | ||||
|   expires: number; | ||||
|   constructor(certInfo: CertInfo) { | ||||
|     this.cert = certInfo; | ||||
|  | @ -52,8 +53,9 @@ export class CertReader { | |||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       const { detail, expires } = this.getCrtDetail(this.cert.crt); | ||||
|       const { detail, effective, expires } = this.getCrtDetail(this.cert.crt); | ||||
|       this.detail = detail; | ||||
|       this.effective = effective.getTime(); | ||||
|       this.expires = expires.getTime(); | ||||
|     } catch (e) { | ||||
|       throw new Error("证书解析失败:" + e.message); | ||||
|  | @ -102,8 +104,9 @@ export class CertReader { | |||
| 
 | ||||
|   static readCertDetail(crt: string) { | ||||
|     const detail = crypto.readCertificateInfo(crt.toString()); | ||||
|     const effective = detail.notBefore; | ||||
|     const expires = detail.notAfter; | ||||
|     return { detail, expires }; | ||||
|     return { detail, effective, expires }; | ||||
|   } | ||||
| 
 | ||||
|   getAllDomains() { | ||||
|  |  | |||
|  | @ -119,6 +119,7 @@ export default { | |||
|     scheduledTaskCount: "Scheduled Task Count", | ||||
|     deployTaskCount: "Deployment Task Count", | ||||
|     remainingValidity: "Remaining Validity", | ||||
|     effectiveTime: "Effective time", | ||||
|     expiryTime: "Expiry Time", | ||||
|     status: "Status", | ||||
|     lastRun: "Last Run", | ||||
|  | @ -250,7 +251,9 @@ export default { | |||
|       ok: "Valid", | ||||
|       expired: "Expired", | ||||
|     }, | ||||
|     certEffectiveTime: "Certificate Effective", | ||||
|     certExpiresTime: "Certificate Expiration", | ||||
|     remainingValidity: "Remaining Validity", | ||||
|     expired: "expired", | ||||
|     days: "days", | ||||
|     lastCheckTime: "Last Check Time", | ||||
|  | @ -465,6 +468,7 @@ export default { | |||
|   validDays: "Valid Days", | ||||
|   expires: " expires", | ||||
|   days: " days", | ||||
|   effectiveTime: "Effective Time", | ||||
|   expireTime: "Expiration Time", | ||||
|   certIssuer: "Certificate Issuer", | ||||
|   applyTime: "Application Time", | ||||
|  |  | |||
|  | @ -125,6 +125,7 @@ export default { | |||
|     scheduledTaskCount: "定时任务数", | ||||
|     deployTaskCount: "部署任务数", | ||||
|     remainingValidity: "到期剩余", | ||||
|     effectiveTime: "生效时间", | ||||
|     expiryTime: "过期时间", | ||||
|     status: "状态", | ||||
|     lastRun: "最后运行", | ||||
|  | @ -255,7 +256,9 @@ export default { | |||
|       ok: "正常", | ||||
|       expired: "过期", | ||||
|     }, | ||||
|     certEffectiveTime: "证书生效时间", | ||||
|     certExpiresTime: "证书到期时间", | ||||
|     remainingValidity: "到期剩余", | ||||
|     expired: "过期", | ||||
|     days: "天", | ||||
|     lastCheckTime: "上次检查时间", | ||||
|  | @ -471,6 +474,7 @@ export default { | |||
|   validDays: "有效天数", | ||||
|   expires: "过期", | ||||
|   days: "天", | ||||
|   effectiveTime: "生效时间", | ||||
|   expireTime: "过期时间", | ||||
|   certIssuer: "证书颁发机构", | ||||
|   applyTime: "申请时间", | ||||
|  |  | |||
|  | @ -220,22 +220,47 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat | |||
|             sorter: true, | ||||
|             conditionalRender: false, | ||||
|             cellRender({ row }) { | ||||
|               const value = row.expiresTime; | ||||
|               if (!value) { | ||||
|               const { | ||||
|                 applyTime, | ||||
|                 effectiveTime, | ||||
|                 expiresTime, | ||||
|               } = row || {}; | ||||
|               if (!expiresTime) { | ||||
|                 return "-"; | ||||
|               } | ||||
|               const expireDate = dayjs(value).format("YYYY-MM-DD"); | ||||
|               const leftDays = dayjs(value).diff(dayjs(), "day"); | ||||
|               // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间
 | ||||
|               const applyDate = dayjs(effectiveTime ?? applyTime ?? Date.now()).format("YYYY-MM-DD"); | ||||
|               // 失效时间
 | ||||
|               const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); | ||||
|               // 有效天数 ps:此处证书最小设置为90d
 | ||||
|               const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); | ||||
|               // 距离失效时间剩余天数
 | ||||
|               const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); | ||||
|               const color = leftDays < 20 ? "red" : "#389e0d"; | ||||
|               const percent = (leftDays / 90) * 100; | ||||
|               const percent = (leftDays / effectiveDays) * 100; | ||||
|               const textColor = leftDays < 20 ? "red" : leftDays > 60 ? "#389e0d" : ""; | ||||
|               const format = () => { | ||||
|                 return <span style={{ color: textColor }}>{`${leftDays}${t("certd.days")}`}</span>; | ||||
|               }; | ||||
|               // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row)
 | ||||
|               return <a-progress title={expireDate + t("certd.expires")} percent={percent} strokeColor={color} format={format} />; | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|         effectiveTime: { | ||||
|           title: t("certd.effectiveTime"), | ||||
|           search: { | ||||
|             show: false, | ||||
|           }, | ||||
|           type: "datetime", | ||||
|           form: { | ||||
|             show: false, | ||||
|           }, | ||||
|           column: { | ||||
|             sorter: true, | ||||
|             show: false, | ||||
|           }, | ||||
|         }, | ||||
|         expiresTime: { | ||||
|           title: t("certd.expireTime"), | ||||
|           search: { | ||||
|  |  | |||
|  | @ -345,25 +345,64 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat | |||
|             align: "center", | ||||
|           }, | ||||
|         }, | ||||
|         certEffectiveTime: { | ||||
|           title: t("certd.monitor.certEffectiveTime"), | ||||
|           search: { | ||||
|             show: false, | ||||
|           }, | ||||
|           type: "datetime", | ||||
|           form: { | ||||
|             show: false, | ||||
|           }, | ||||
|           column: { | ||||
|             sorter: true, | ||||
|             width: 155, | ||||
|           }, | ||||
|         }, | ||||
|         certExpiresTime: { | ||||
|           title: t("certd.monitor.certExpiresTime"), | ||||
|           search: { | ||||
|             show: false, | ||||
|           }, | ||||
|           type: "datetime", | ||||
|           form: { | ||||
|             show: false, | ||||
|           }, | ||||
|           column: { | ||||
|             sorter: true, | ||||
|             width: 155, | ||||
|           }, | ||||
|         }, | ||||
|         remainingValidity: { | ||||
|           title: t("certd.monitor.remainingValidity"), | ||||
|           search: { | ||||
|             show: false, | ||||
|           }, | ||||
|           type: "date", | ||||
|           form: { | ||||
|             show: false, | ||||
|           }, | ||||
|           column: { | ||||
|             sorter: true, | ||||
|             cellRender({ value }) { | ||||
|               if (!value) { | ||||
|             conditionalRender: false, | ||||
|             cellRender({ row }) { | ||||
|               const { | ||||
|                 certEffectiveTime: effectiveTime, | ||||
|                 certExpiresTime: expiresTime, | ||||
|               } = row || {}; | ||||
|               if (!expiresTime) { | ||||
|                 return "-"; | ||||
|               } | ||||
|               const expireDate = dayjs(value).format("YYYY-MM-DD"); | ||||
|               const leftDays = dayjs(value).diff(dayjs(), "day"); | ||||
|               // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间
 | ||||
|               const applyDate = dayjs(effectiveTime ?? Date.now()).format("YYYY-MM-DD"); | ||||
|               // 失效时间
 | ||||
|               const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); | ||||
|               // 有效天数 ps:此处证书最小设置为90d
 | ||||
|               const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); | ||||
|               // 距离失效时间剩余天数
 | ||||
|               const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); | ||||
|               const color = leftDays < 20 ? "red" : "#389e0d"; | ||||
|               const percent = (leftDays / 90) * 100; | ||||
|               const percent = (leftDays / effectiveDays) * 100; | ||||
|               // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row)
 | ||||
|               return <a-progress title={expireDate + t("certd.monitor.expired")} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}${t("certd.monitor.days")}`} />; | ||||
|             }, | ||||
|           }, | ||||
|  |  | |||
|  | @ -366,23 +366,49 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys | |||
|           }, | ||||
|           column: { | ||||
|             cellRender({ row }) { | ||||
|               const value = row.lastVars?.certExpiresTime; | ||||
|               if (!value) { | ||||
|               const { | ||||
|                 certEffectiveTime: effectiveTime, | ||||
|                 certExpiresTime: expiresTime, | ||||
|               } = row?.lastVars || {}; | ||||
|               if (!expiresTime) { | ||||
|                 return "-"; | ||||
|               } | ||||
|               const expireDate = dayjs(value).format("YYYY-MM-DD"); | ||||
|               const leftDays = dayjs(value).diff(dayjs(), "day"); | ||||
|               // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间
 | ||||
|               const applyDate = dayjs(effectiveTime ?? Date.now()).format("YYYY-MM-DD"); | ||||
|               // 失效时间
 | ||||
|               const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); | ||||
|               // 有效天数 ps:此处证书最小设置为90d
 | ||||
|               const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); | ||||
|               // 距离失效时间剩余天数
 | ||||
|               const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); | ||||
|               const color = leftDays < 20 ? "red" : "#389e0d"; | ||||
|               const percent = (leftDays / 90) * 100; | ||||
|               const percent = (leftDays / effectiveDays) * 100; | ||||
|               const textColor = leftDays < 20 ? "red" : leftDays > 60 ? "#389e0d" : ""; | ||||
|               const format = () => { | ||||
|                 return <span style={{ color: textColor }}>{`${leftDays}${t("certd.days")}`}</span>; | ||||
|               }; | ||||
|               // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row)
 | ||||
|               return <a-progress title={expireDate + t("certd.expires")} percent={percent} strokeColor={color} format={format} />; | ||||
|             }, | ||||
|             width: 150, | ||||
|           }, | ||||
|         }, | ||||
|         "lastVars.certEffectiveTime": { | ||||
|           title: t("certd.fields.effectiveTime"), | ||||
|           search: { | ||||
|             show: false, | ||||
|           }, | ||||
|           type: "datetime", | ||||
|           form: { | ||||
|             show: false, | ||||
|           }, | ||||
|           column: { | ||||
|             sorter: false, | ||||
|             show: false, | ||||
|             width: 150, | ||||
|             align: "center", | ||||
|           }, | ||||
|         }, | ||||
|         "lastVars.certExpiresTime": { | ||||
|           title: t("certd.fields.expiryTime"), | ||||
|           search: { | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| ALTER TABLE cd_cert_info ADD COLUMN effective_time INTEGER; | ||||
| ALTER TABLE cd_site_info ADD COLUMN cert_effective_time INTEGER; | ||||
|  | @ -0,0 +1,2 @@ | |||
| ALTER TABLE cd_cert_info ADD COLUMN effective_time INTEGER; | ||||
| ALTER TABLE cd_site_info ADD COLUMN cert_effective_time INTEGER; | ||||
|  | @ -0,0 +1,2 @@ | |||
| ALTER TABLE cd_cert_info ADD COLUMN effective_time INTEGER; | ||||
| ALTER TABLE cd_site_info ADD COLUMN cert_effective_time INTEGER; | ||||
|  | @ -30,6 +30,9 @@ export class CertInfoEntity { | |||
|   @Column({ name: 'cert_provider', comment: '证书颁发机构' }) | ||||
|   certProvider: string; | ||||
| 
 | ||||
|   @Column({ name: 'effective_time', comment: '生效时间' }) | ||||
|   effectiveTime: number; | ||||
| 
 | ||||
|   @Column({ name: 'expires_time', comment: '过期时间' }) | ||||
|   expiresTime: number; | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ export class SiteInfoEntity { | |||
|   @Column({ name: 'cert_provider', comment: '证书颁发机构', length: 100 }) | ||||
|   certProvider: string; | ||||
| 
 | ||||
|   @Column({ name: 'cert_effective_time', comment: '证书生效时间' }) | ||||
|   certEffectiveTime: number; | ||||
|   @Column({ name: 'cert_expires_time', comment: '证书到期时间' }) | ||||
|   certExpiresTime: number; | ||||
|   @Column({ name: 'last_check_time', comment: '上次检查时间' }) | ||||
|  |  | |||
|  | @ -164,6 +164,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> { | |||
|     bean.domains = domains.join(','); | ||||
|     bean.domain = domains[0]; | ||||
|     bean.domainCount = domains.length; | ||||
|     bean.effectiveTime = certReader.effective; | ||||
|     bean.expiresTime = certReader.expires; | ||||
|     bean.certProvider = certReader.detail.issuer.commonName; | ||||
|     bean.userId = userId | ||||
|  |  | |||
|  | @ -134,6 +134,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> { | |||
|       if (!certi) { | ||||
|         throw new Error("没有发现证书"); | ||||
|       } | ||||
|       const effective = certi.valid_from; | ||||
|       const expires = certi.valid_to; | ||||
|       const allDomains = certi.subjectaltname?.replaceAll("DNS:", "").split(",") || []; | ||||
|       const mainDomain = certi.subject?.CN; | ||||
|  | @ -149,12 +150,13 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> { | |||
|         certDomains: domains.join(","), | ||||
|         certStatus: status, | ||||
|         certProvider: issuer, | ||||
|         certEffectiveTime: dayjs(effective).valueOf(), | ||||
|         certExpiresTime: dayjs(expires).valueOf(), | ||||
|         lastCheckTime: dayjs().valueOf(), | ||||
|         error: null, | ||||
|         checkStatus: "ok" | ||||
|       }; | ||||
|       logger.info(`测试站点成功:id=${updateData.id},site=${site.name},expiresTime=${updateData.certExpiresTime}`) | ||||
|       logger.info(`测试站点成功:id=${updateData.id},site=${site.name},certEffectiveTime=${updateData.certEffectiveTime},expiresTime=${updateData.certExpiresTime}`) | ||||
|       if (site.ipCheck) { | ||||
|         delete updateData.checkStatus | ||||
|       } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 ahe
						ahe