浏览代码

Show cert expiry date in yellow when < 30 days

Jamie Curnow 1 月之前
父节点
当前提交
ca3c9aa39a
共有 2 个文件被更改,包括 89 次插入222 次删除
  1. 84 220
      backend/internal/certificate.js
  2. 5 2
      frontend/src/components/Table/Formatter/DateFormatter.tsx

+ 84 - 220
backend/internal/certificate.js

@@ -1,9 +1,9 @@
 import fs from "node:fs";
 import fs from "node:fs";
 import https from "node:https";
 import https from "node:https";
+import path from "path";
 import archiver from "archiver";
 import archiver from "archiver";
 import _ from "lodash";
 import _ from "lodash";
 import moment from "moment";
 import moment from "moment";
-import path from "path";
 import tempWrite from "temp-write";
 import tempWrite from "temp-write";
 import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
 import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
 import { installPlugin } from "../lib/certbot.js";
 import { installPlugin } from "../lib/certbot.js";
@@ -28,11 +28,7 @@ const omissions = () => {
 };
 };
 
 
 const internalCertificate = {
 const internalCertificate = {
-	allowedSslFiles: [
-		"certificate",
-		"certificate_key",
-		"intermediate_certificate",
-	],
+	allowedSslFiles: ["certificate", "certificate_key", "intermediate_certificate"],
 	intervalTimeout: 1000 * 60 * 60, // 1 hour
 	intervalTimeout: 1000 * 60 * 60, // 1 hour
 	interval: null,
 	interval: null,
 	intervalProcessing: false,
 	intervalProcessing: false,
@@ -59,10 +55,7 @@ const internalCertificate = {
 			);
 			);
 
 
 			const expirationThreshold = moment()
 			const expirationThreshold = moment()
-				.add(
-					internalCertificate.renewBeforeExpirationBy[0],
-					internalCertificate.renewBeforeExpirationBy[1],
-				)
+				.add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1])
 				.format("YYYY-MM-DD HH:mm:ss");
 				.format("YYYY-MM-DD HH:mm:ss");
 
 
 			// Fetch all the letsencrypt certs from the db that will expire within the configured threshold
 			// Fetch all the letsencrypt certs from the db that will expire within the configured threshold
@@ -129,10 +122,7 @@ const internalCertificate = {
 		}
 		}
 
 
 		// this command really should clean up and delete the cert if it can't fully succeed
 		// this command really should clean up and delete the cert if it can't fully succeed
-		const certificate = await certificateModel
-			.query()
-			.insertAndFetch(data)
-			.then(utils.omitRow(omissions()));
+		const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
 
 
 		try {
 		try {
 			if (certificate.provider === "letsencrypt") {
 			if (certificate.provider === "letsencrypt") {
@@ -146,18 +136,12 @@ const internalCertificate = {
 				// 6. Re-instate previously disabled hosts
 				// 6. Re-instate previously disabled hosts
 
 
 				// 1. Find out any hosts that are using any of the hostnames in this cert
 				// 1. Find out any hosts that are using any of the hostnames in this cert
-				const inUseResult = await internalHost.getHostsWithDomains(
-					certificate.domain_names,
-				);
+				const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names);
 
 
 				// 2. Disable them in nginx temporarily
 				// 2. Disable them in nginx temporarily
 				await internalCertificate.disableInUseHosts(inUseResult);
 				await internalCertificate.disableInUseHosts(inUseResult);
 
 
-				const user = await userModel
-					.query()
-					.where("is_deleted", 0)
-					.andWhere("id", data.owner_user_id)
-					.first();
+				const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
 				if (!user || !user.email) {
 				if (!user || !user.email) {
 					throw new error.ValidationError(
 					throw new error.ValidationError(
 						"A valid email address must be set on your user account to use Let's Encrypt",
 						"A valid email address must be set on your user account to use Let's Encrypt",
@@ -169,10 +153,7 @@ const internalCertificate = {
 					try {
 					try {
 						await internalNginx.reload();
 						await internalNginx.reload();
 						// 4. Request cert
 						// 4. Request cert
-						await internalCertificate.requestLetsEncryptSslWithDnsChallenge(
-							certificate,
-							user.email,
-						);
+						await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
 						await internalNginx.reload();
 						await internalNginx.reload();
 						// 6. Re-instate previously disabled hosts
 						// 6. Re-instate previously disabled hosts
 						await internalCertificate.enableInUseHosts(inUseResult);
 						await internalCertificate.enableInUseHosts(inUseResult);
@@ -189,10 +170,7 @@ const internalCertificate = {
 						await internalNginx.reload();
 						await internalNginx.reload();
 						setTimeout(() => {}, 5000);
 						setTimeout(() => {}, 5000);
 						// 4. Request cert
 						// 4. Request cert
-						await internalCertificate.requestLetsEncryptSsl(
-							certificate,
-							user.email,
-						);
+						await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
 						// 5. Remove LE config
 						// 5. Remove LE config
 						await internalNginx.deleteLetsEncryptRequestConfig(certificate);
 						await internalNginx.deleteLetsEncryptRequestConfig(certificate);
 						await internalNginx.reload();
 						await internalNginx.reload();
@@ -216,9 +194,7 @@ const internalCertificate = {
 					const savedRow = await certificateModel
 					const savedRow = await certificateModel
 						.query()
 						.query()
 						.patchAndFetchById(certificate.id, {
 						.patchAndFetchById(certificate.id, {
-							expires_on: moment(certInfo.dates.to, "X").format(
-								"YYYY-MM-DD HH:mm:ss",
-							),
+							expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
 						})
 						})
 						.then(utils.omitRow(omissions()));
 						.then(utils.omitRow(omissions()));
 
 
@@ -346,9 +322,7 @@ const internalCertificate = {
 		if (certificate.provider === "letsencrypt") {
 		if (certificate.provider === "letsencrypt") {
 			const zipDirectory = internalCertificate.getLiveCertPath(data.id);
 			const zipDirectory = internalCertificate.getLiveCertPath(data.id);
 			if (!fs.existsSync(zipDirectory)) {
 			if (!fs.existsSync(zipDirectory)) {
-				throw new error.ItemNotFoundError(
-					`Certificate ${certificate.nice_name} does not exists`,
-				);
+				throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`);
 			}
 			}
 
 
 			const certFiles = fs
 			const certFiles = fs
@@ -365,9 +339,7 @@ const internalCertificate = {
 				fileName: opName,
 				fileName: opName,
 			};
 			};
 		}
 		}
-		throw new error.ValidationError(
-			"Only Let'sEncrypt certificates can be downloaded",
-		);
+		throw new error.ValidationError("Only Let'sEncrypt certificates can be downloaded");
 	},
 	},
 
 
 	/**
 	/**
@@ -472,10 +444,7 @@ const internalCertificate = {
 	 * @returns {Promise}
 	 * @returns {Promise}
 	 */
 	 */
 	getCount: async (userId, visibility) => {
 	getCount: async (userId, visibility) => {
-		const query = certificateModel
-			.query()
-			.count("id as count")
-			.where("is_deleted", 0);
+		const query = certificateModel.query().count("id as count").where("is_deleted", 0);
 
 
 		if (visibility !== "all") {
 		if (visibility !== "all") {
 			query.andWhere("owner_user_id", userId);
 			query.andWhere("owner_user_id", userId);
@@ -523,17 +492,13 @@ const internalCertificate = {
 			});
 			});
 		}).then(() => {
 		}).then(() => {
 			return new Promise((resolve, reject) => {
 			return new Promise((resolve, reject) => {
-				fs.writeFile(
-					`${dir}/privkey.pem`,
-					certificate.meta.certificate_key,
-					(err) => {
-						if (err) {
-							reject(err);
-						} else {
-							resolve();
-						}
-					},
-				);
+				fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => {
+					if (err) {
+						reject(err);
+					} else {
+						resolve();
+					}
+				});
 			});
 			});
 		});
 		});
 	},
 	},
@@ -606,9 +571,7 @@ const internalCertificate = {
 	upload: async (access, data) => {
 	upload: async (access, data) => {
 		const row = await internalCertificate.get(access, { id: data.id });
 		const row = await internalCertificate.get(access, { id: data.id });
 		if (row.provider !== "other") {
 		if (row.provider !== "other") {
-			throw new error.ValidationError(
-				"Cannot upload certificates for this type of provider",
-			);
+			throw new error.ValidationError("Cannot upload certificates for this type of provider");
 		}
 		}
 
 
 		const validations = await internalCertificate.validate(data);
 		const validations = await internalCertificate.validate(data);
@@ -624,9 +587,7 @@ const internalCertificate = {
 
 
 		const certificate = await internalCertificate.update(access, {
 		const certificate = await internalCertificate.update(access, {
 			id: data.id,
 			id: data.id,
-			expires_on: moment(validations.certificate.dates.to, "X").format(
-				"YYYY-MM-DD HH:mm:ss",
-			),
+			expires_on: moment(validations.certificate.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
 			domain_names: [validations.certificate.cn],
 			domain_names: [validations.certificate.cn],
 			meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
 			meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
 		});
 		});
@@ -651,9 +612,7 @@ const internalCertificate = {
 		}, 10000);
 		}, 10000);
 
 
 		try {
 		try {
-			const result = await utils.exec(
-				`openssl pkey -in ${filepath} -check -noout 2>&1 `,
-			);
+			const result = await utils.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `);
 			clearTimeout(failTimeout);
 			clearTimeout(failTimeout);
 			if (!result.toLowerCase().includes("key is valid")) {
 			if (!result.toLowerCase().includes("key is valid")) {
 				throw new error.ValidationError(`Result Validation Error: ${result}`);
 				throw new error.ValidationError(`Result Validation Error: ${result}`);
@@ -663,10 +622,7 @@ const internalCertificate = {
 		} catch (err) {
 		} catch (err) {
 			clearTimeout(failTimeout);
 			clearTimeout(failTimeout);
 			fs.unlinkSync(filepath);
 			fs.unlinkSync(filepath);
-			throw new error.ValidationError(
-				`Certificate Key is not valid (${err.message})`,
-				err,
-			);
+			throw new error.ValidationError(`Certificate Key is not valid (${err.message})`, err);
 		}
 		}
 	},
 	},
 
 
@@ -680,10 +636,7 @@ const internalCertificate = {
 	getCertificateInfo: async (certificate, throwExpired) => {
 	getCertificateInfo: async (certificate, throwExpired) => {
 		try {
 		try {
 			const filepath = await tempWrite(certificate, "/tmp");
 			const filepath = await tempWrite(certificate, "/tmp");
-			const certData = await internalCertificate.getCertificateInfoFromFile(
-				filepath,
-				throwExpired,
-			);
+			const certData = await internalCertificate.getCertificateInfoFromFile(filepath, throwExpired);
 			fs.unlinkSync(filepath);
 			fs.unlinkSync(filepath);
 			return certData;
 			return certData;
 		} catch (err) {
 		} catch (err) {
@@ -703,13 +656,7 @@ const internalCertificate = {
 		const certData = {};
 		const certData = {};
 
 
 		try {
 		try {
-			const result = await utils.execFile("openssl", [
-				"x509",
-				"-in",
-				certificateFile,
-				"-subject",
-				"-noout",
-			]);
+			const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
 			// Examples:
 			// Examples:
 			// subject=CN = *.jc21.com
 			// subject=CN = *.jc21.com
 			// subject=CN = something.example.com
 			// subject=CN = something.example.com
@@ -719,13 +666,7 @@ const internalCertificate = {
 				certData.cn = match[1];
 				certData.cn = match[1];
 			}
 			}
 
 
-			const result2 = await utils.execFile("openssl", [
-				"x509",
-				"-in",
-				certificateFile,
-				"-issuer",
-				"-noout",
-			]);
+			const result2 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-issuer", "-noout"]);
 			// Examples:
 			// Examples:
 			// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
 			// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
 			// issuer=C = US, O = Let's Encrypt, CN = E5
 			// issuer=C = US, O = Let's Encrypt, CN = E5
@@ -736,13 +677,7 @@ const internalCertificate = {
 				certData.issuer = match2[1];
 				certData.issuer = match2[1];
 			}
 			}
 
 
-			const result3 = await utils.execFile("openssl", [
-				"x509",
-				"-in",
-				certificateFile,
-				"-dates",
-				"-noout",
-			]);
+			const result3 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-dates", "-noout"]);
 			// notBefore=Jul 14 04:04:29 2018 GMT
 			// notBefore=Jul 14 04:04:29 2018 GMT
 			// notAfter=Oct 12 04:04:29 2018 GMT
 			// notAfter=Oct 12 04:04:29 2018 GMT
 			let validFrom = null;
 			let validFrom = null;
@@ -754,10 +689,7 @@ const internalCertificate = {
 				const match = regex.exec(str.trim());
 				const match = regex.exec(str.trim());
 
 
 				if (match && typeof match[2] !== "undefined") {
 				if (match && typeof match[2] !== "undefined") {
-					const date = Number.parseInt(
-						moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"),
-						10,
-					);
+					const date = Number.parseInt(moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"), 10);
 
 
 					if (match[1].toLowerCase() === "notbefore") {
 					if (match[1].toLowerCase() === "notbefore") {
 						validFrom = date;
 						validFrom = date;
@@ -769,15 +701,10 @@ const internalCertificate = {
 			});
 			});
 
 
 			if (!validFrom || !validTo) {
 			if (!validFrom || !validTo) {
-				throw new error.ValidationError(
-					`Could not determine dates from certificate: ${result}`,
-				);
+				throw new error.ValidationError(`Could not determine dates from certificate: ${result}`);
 			}
 			}
 
 
-			if (
-				throw_expired &&
-				validTo < Number.parseInt(moment().format("X"), 10)
-			) {
+			if (throw_expired && validTo < Number.parseInt(moment().format("X"), 10)) {
 				throw new error.ValidationError("Certificate has expired");
 				throw new error.ValidationError("Certificate has expired");
 			}
 			}
 
 
@@ -788,10 +715,7 @@ const internalCertificate = {
 
 
 			return certData;
 			return certData;
 		} catch (err) {
 		} catch (err) {
-			throw new error.ValidationError(
-				`Certificate is not valid (${err.message})`,
-				err,
-			);
+			throw new error.ValidationError(`Certificate is not valid (${err.message})`, err);
 		}
 		}
 	},
 	},
 
 
@@ -872,11 +796,7 @@ const internalCertificate = {
 
 
 		const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
 		const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
 		fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true });
 		fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true });
-		fs.writeFileSync(
-			credentialsLocation,
-			certificate.meta.dns_provider_credentials,
-			{ mode: 0o600 },
-		);
+		fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 });
 
 
 		// Whether the plugin has a --<name>-credentials argument
 		// Whether the plugin has a --<name>-credentials argument
 		const hasConfigArg = certificate.meta.dns_provider !== "route53";
 		const hasConfigArg = certificate.meta.dns_provider !== "route53";
@@ -903,10 +823,7 @@ const internalCertificate = {
 		];
 		];
 
 
 		if (hasConfigArg) {
 		if (hasConfigArg) {
-			args.push(
-				`--${dnsPlugin.full_plugin_name}-credentials`,
-				credentialsLocation,
-			);
+			args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation);
 		}
 		}
 		if (certificate.meta.propagation_seconds !== undefined) {
 		if (certificate.meta.propagation_seconds !== undefined) {
 			args.push(
 			args.push(
@@ -915,10 +832,7 @@ const internalCertificate = {
 			);
 			);
 		}
 		}
 
 
-		const adds = internalCertificate.getAdditionalCertbotArgs(
-			certificate.id,
-			certificate.meta.dns_provider,
-		);
+		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
 		args.push(...adds.args);
 		args.push(...adds.args);
 
 
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -954,13 +868,9 @@ const internalCertificate = {
 				`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
 				`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
 			);
 			);
 
 
-			const updatedCertificate = await certificateModel
-				.query()
-				.patchAndFetchById(certificate.id, {
-					expires_on: moment(certInfo.dates.to, "X").format(
-						"YYYY-MM-DD HH:mm:ss",
-					),
-				});
+			const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, {
+				expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
+			});
 
 
 			// Add to audit log
 			// Add to audit log
 			await internalAuditLog.add(access, {
 			await internalAuditLog.add(access, {
@@ -971,11 +881,9 @@ const internalCertificate = {
 			});
 			});
 
 
 			return updatedCertificate;
 			return updatedCertificate;
-		} else {
-			throw new error.ValidationError(
-				"Only Let'sEncrypt certificates can be renewed",
-			);
 		}
 		}
+
+		throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed");
 	},
 	},
 
 
 	/**
 	/**
@@ -1004,10 +912,7 @@ const internalCertificate = {
 			"--disable-hook-validation",
 			"--disable-hook-validation",
 		];
 		];
 
 
-		const adds = internalCertificate.getAdditionalCertbotArgs(
-			certificate.id,
-			certificate.meta.dns_provider,
-		);
+		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
 		args.push(...adds.args);
 		args.push(...adds.args);
 
 
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1048,10 +953,7 @@ const internalCertificate = {
 			"--no-random-sleep-on-renew",
 			"--no-random-sleep-on-renew",
 		];
 		];
 
 
-		const adds = internalCertificate.getAdditionalCertbotArgs(
-			certificate.id,
-			certificate.meta.dns_provider,
-		);
+		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
 		args.push(...adds.args);
 		args.push(...adds.args);
 
 
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1091,9 +993,7 @@ const internalCertificate = {
 
 
 		try {
 		try {
 			const result = await utils.execFile(certbotCommand, args, adds.opts);
 			const result = await utils.execFile(certbotCommand, args, adds.opts);
-			await utils.exec(
-				`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`,
-			);
+			await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`);
 			logger.info(result);
 			logger.info(result);
 			return result;
 			return result;
 		} catch (err) {
 		} catch (err) {
@@ -1110,10 +1010,7 @@ const internalCertificate = {
 	 */
 	 */
 	hasLetsEncryptSslCerts: (certificate) => {
 	hasLetsEncryptSslCerts: (certificate) => {
 		const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
 		const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
-		return (
-			fs.existsSync(`${letsencryptPath}/fullchain.pem`) &&
-			fs.existsSync(`${letsencryptPath}/privkey.pem`)
-		);
+		return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`);
 	},
 	},
 
 
 	/**
 	/**
@@ -1127,24 +1024,15 @@ const internalCertificate = {
 	disableInUseHosts: async (inUseResult) => {
 	disableInUseHosts: async (inUseResult) => {
 		if (inUseResult?.total_count) {
 		if (inUseResult?.total_count) {
 			if (inUseResult?.proxy_hosts.length) {
 			if (inUseResult?.proxy_hosts.length) {
-				await internalNginx.bulkDeleteConfigs(
-					"proxy_host",
-					inUseResult.proxy_hosts,
-				);
+				await internalNginx.bulkDeleteConfigs("proxy_host", inUseResult.proxy_hosts);
 			}
 			}
 
 
 			if (inUseResult?.redirection_hosts.length) {
 			if (inUseResult?.redirection_hosts.length) {
-				await internalNginx.bulkDeleteConfigs(
-					"redirection_host",
-					inUseResult.redirection_hosts,
-				);
+				await internalNginx.bulkDeleteConfigs("redirection_host", inUseResult.redirection_hosts);
 			}
 			}
 
 
 			if (inUseResult?.dead_hosts.length) {
 			if (inUseResult?.dead_hosts.length) {
-				await internalNginx.bulkDeleteConfigs(
-					"dead_host",
-					inUseResult.dead_hosts,
-				);
+				await internalNginx.bulkDeleteConfigs("dead_host", inUseResult.dead_hosts);
 			}
 			}
 		}
 		}
 	},
 	},
@@ -1160,24 +1048,15 @@ const internalCertificate = {
 	enableInUseHosts: async (inUseResult) => {
 	enableInUseHosts: async (inUseResult) => {
 		if (inUseResult.total_count) {
 		if (inUseResult.total_count) {
 			if (inUseResult.proxy_hosts.length) {
 			if (inUseResult.proxy_hosts.length) {
-				await internalNginx.bulkGenerateConfigs(
-					"proxy_host",
-					inUseResult.proxy_hosts,
-				);
+				await internalNginx.bulkGenerateConfigs("proxy_host", inUseResult.proxy_hosts);
 			}
 			}
 
 
 			if (inUseResult.redirection_hosts.length) {
 			if (inUseResult.redirection_hosts.length) {
-				await internalNginx.bulkGenerateConfigs(
-					"redirection_host",
-					inUseResult.redirection_hosts,
-				);
+				await internalNginx.bulkGenerateConfigs("redirection_host", inUseResult.redirection_hosts);
 			}
 			}
 
 
 			if (inUseResult.dead_hosts.length) {
 			if (inUseResult.dead_hosts.length) {
-				await internalNginx.bulkGenerateConfigs(
-					"dead_host",
-					inUseResult.dead_hosts,
-				);
+				await internalNginx.bulkGenerateConfigs("dead_host", inUseResult.dead_hosts);
 			}
 			}
 		}
 		}
 	},
 	},
@@ -1192,8 +1071,7 @@ const internalCertificate = {
 		await access.can("certificates:list");
 		await access.can("certificates:list");
 
 
 		// Create a test challenge file
 		// Create a test challenge file
-		const testChallengeDir =
-			"/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
+		const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
 		const testChallengeFile = `${testChallengeDir}/test-challenge`;
 		const testChallengeFile = `${testChallengeDir}/test-challenge`;
 		fs.mkdirSync(testChallengeDir, { recursive: true });
 		fs.mkdirSync(testChallengeDir, { recursive: true });
 		fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" });
 		fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" });
@@ -1223,42 +1101,38 @@ const internalCertificate = {
 		};
 		};
 
 
 		const result = await new Promise((resolve) => {
 		const result = await new Promise((resolve) => {
-			const req = https.request(
-				"https://www.site24x7.com/tools/restapi-tester",
-				options,
-				(res) => {
-					let responseBody = "";
-
-					res.on("data", (chunk) => {
-						responseBody = responseBody + chunk;
-					});
+			const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => {
+				let responseBody = "";
+
+				res.on("data", (chunk) => {
+					responseBody = responseBody + chunk;
+				});
 
 
-					res.on("end", () => {
-						try {
-							const parsedBody = JSON.parse(`${responseBody}`);
-							if (res.statusCode !== 200) {
-								logger.warn(
-									`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`,
-								);
-								resolve(undefined);
-							} else {
-								resolve(parsedBody);
-							}
-						} catch (err) {
-							if (res.statusCode !== 200) {
-								logger.warn(
-									`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
-								);
-							} else {
-								logger.warn(
-									`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
-								);
-							}
+				res.on("end", () => {
+					try {
+						const parsedBody = JSON.parse(`${responseBody}`);
+						if (res.statusCode !== 200) {
+							logger.warn(
+								`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`,
+							);
 							resolve(undefined);
 							resolve(undefined);
+						} else {
+							resolve(parsedBody);
 						}
 						}
-					});
-				},
-			);
+					} catch (err) {
+						if (res.statusCode !== 200) {
+							logger.warn(
+								`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
+							);
+						} else {
+							logger.warn(
+								`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
+							);
+						}
+						resolve(undefined);
+					}
+				});
+			});
 
 
 			// Make sure to write the request body.
 			// Make sure to write the request body.
 			req.write(formBody);
 			req.write(formBody);
@@ -1279,10 +1153,7 @@ const internalCertificate = {
 			);
 			);
 			return `other:${result.error.msg}`;
 			return `other:${result.error.msg}`;
 		}
 		}
-		if (
-			`${result.responsecode}` === "200" &&
-			result.htmlresponse === "Success"
-		) {
+		if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") {
 			// Server exists and has responded with the correct data
 			// Server exists and has responded with the correct data
 			return "ok";
 			return "ok";
 		}
 		}
@@ -1296,26 +1167,19 @@ const internalCertificate = {
 		}
 		}
 		if (`${result.responsecode}` === "404") {
 		if (`${result.responsecode}` === "404") {
 			// Server exists but responded with a 404
 			// Server exists but responded with a 404
-			logger.info(
-				`HTTP challenge test failed for domain ${domain} because code 404 was returned`,
-			);
+			logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
 			return "404";
 			return "404";
 		}
 		}
 		if (
 		if (
 			`${result.responsecode}` === "0" ||
 			`${result.responsecode}` === "0" ||
-			(typeof result.reason === "string" &&
-				result.reason.toLowerCase() === "host unavailable")
+			(typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable")
 		) {
 		) {
 			// Server does not exist at domain
 			// Server does not exist at domain
-			logger.info(
-				`HTTP challenge test failed for domain ${domain} the host was not found`,
-			);
+			logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
 			return "no-host";
 			return "no-host";
 		}
 		}
 		// Other errors
 		// Other errors
-		logger.info(
-			`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
-		);
+		logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
 		return `other:${result.responsecode}`;
 		return `other:${result.responsecode}`;
 	},
 	},
 
 

+ 5 - 2
frontend/src/components/Table/Formatter/DateFormatter.tsx

@@ -1,15 +1,18 @@
 import cn from "classnames";
 import cn from "classnames";
-import { isPast, parseISO } from "date-fns";
+import { differenceInDays, isPast, parseISO } from "date-fns";
 import { DateTimeFormat } from "src/locale";
 import { DateTimeFormat } from "src/locale";
 
 
 interface Props {
 interface Props {
 	value: string;
 	value: string;
 	highlightPast?: boolean;
 	highlightPast?: boolean;
+	highlistNearlyExpired?: boolean;
 }
 }
-export function DateFormatter({ value, highlightPast }: Props) {
+export function DateFormatter({ value, highlightPast, highlistNearlyExpired }: Props) {
 	const dateIsPast = isPast(parseISO(value));
 	const dateIsPast = isPast(parseISO(value));
+	const days = differenceInDays(parseISO(value), new Date());
 	const cl = cn({
 	const cl = cn({
 		"text-danger": highlightPast && dateIsPast,
 		"text-danger": highlightPast && dateIsPast,
+		"text-warning": highlistNearlyExpired && !dateIsPast && days <= 30 && days >= 0,
 	});
 	});
 	return <span className={cl}>{DateTimeFormat(value)}</span>;
 	return <span className={cl}>{DateTimeFormat(value)}</span>;
 }
 }