From a117add9081443109ec8b221e08c942b2cb14eed Mon Sep 17 00:00:00 2001 From: Shaan Date: Fri, 9 Jan 2026 09:18:17 +0600 Subject: [PATCH] fix: Proper processing of date fields (Domain Expiry) with cleanup of unnecessary Date comparison functions (#6638) Co-authored-by: Frank Elsinga Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- server/model/domain_expiry.js | 25 +++++++++++++------------ server/util-server.js | 30 ++---------------------------- test/backend-test/test-domain.js | 9 +++++---- test/backend-test/test-util.js | 21 --------------------- 4 files changed, 20 insertions(+), 65 deletions(-) diff --git a/server/model/domain_expiry.js b/server/model/domain_expiry.js index 1bb6c1f74..91d5b54e3 100644 --- a/server/model/domain_expiry.js +++ b/server/model/domain_expiry.js @@ -2,10 +2,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const { R } = require("redbean-node"); const { log, TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD } = require("../../src/util"); const { parse: parseTld } = require("tldts"); -const { getDaysRemaining, getDaysBetween, setting, setSetting } = require("../util-server"); +const { setting, setSetting } = require("../util-server"); const { Notification } = require("../notification"); const { default: NodeFetchCache, MemoryCache } = require("node-fetch-cache"); const TranslatableError = require("../translatable-error"); +const dayjs = require("dayjs"); const cachedFetch = process.env.NODE_ENV ? NodeFetchCache.create({ @@ -199,18 +200,18 @@ class DomainExpiry extends BeanModel { * @returns {Promise} Domain expiry bean */ static async findByDomainNameOrCreate(domainName) { - const existing = await DomainExpiry.findByName(domainName); - if (existing) { - return existing; + let domain = await DomainExpiry.findByName(domainName); + if (!domain && domainName) { + domain = await DomainExpiry.createByName(domainName); } - return DomainExpiry.createByName(domainName); + return domain; } /** * @returns {number} number of days remaining before expiry */ get daysRemaining() { - return getDaysRemaining(new Date(), new Date(this.expiry)); + return dayjs.utc(this.expiry).diff(dayjs.utc(), "day"); } /** @@ -227,20 +228,20 @@ class DomainExpiry extends BeanModel { */ static async checkExpiry(domainName) { let bean = await DomainExpiry.findByDomainNameOrCreate(domainName); - let expiryDate; - if (bean?.lastCheck && getDaysBetween(new Date(bean.lastCheck), new Date()) < 1) { + + if (bean?.lastCheck && dayjs.utc(bean.lastCheck).diff(dayjs.utc(), "day") < 1) { log.debug("domain_expiry", `Domain expiry already checked recently for ${bean.domain}, won't re-check.`); return bean.expiry; } else if (bean) { expiryDate = await bean.getExpiryDate(); - if (new Date(expiryDate) > new Date(bean.expiry)) { + if (dayjs.utc(expiryDate).isAfter(dayjs.utc(bean.expiry))) { bean.lastExpiryNotificationSent = null; } - bean.expiry = expiryDate; - bean.lastCheck = new Date(); + bean.expiry = R.isoDateTimeMillis(expiryDate); + bean.lastCheck = R.isoDateTimeMillis(dayjs.utc()); await R.store(bean); } @@ -272,7 +273,7 @@ class DomainExpiry extends BeanModel { return; } - const daysRemaining = getDaysRemaining(new Date(), domain.expiry); + const daysRemaining = domain.daysRemaining; const lastSent = domain.lastExpiryNotificationSent; log.debug("domain_expiry", `${domainName} expires in ${daysRemaining} days`); diff --git a/server/util-server.js b/server/util-server.js index 3429606c6..ae792ad85 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -26,6 +26,7 @@ const { }, } = require("node-radius-utils"); const dayjs = require("dayjs"); +dayjs.extend(require("dayjs/plugin/utc")); // SASLOptions used in JSDoc // eslint-disable-next-line no-unused-vars @@ -393,33 +394,6 @@ exports.setSettings = async function (type, data) { await Settings.setSettings(type, data); }; -// ssl-checker by @dyaa -//https://github.com/dyaa/ssl-checker/blob/master/src/index.ts - -/** - * Get number of days between two dates - * @param {Date} validFrom Start date - * @param {Date} validTo End date - * @returns {number} Number of days - */ -const getDaysBetween = (validFrom, validTo) => Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); -exports.getDaysBetween = getDaysBetween; - -/** - * Get days remaining from a time range - * @param {Date} validFrom Start date - * @param {Date} validTo End date - * @returns {number} Number of days remaining - */ -const getDaysRemaining = (validFrom, validTo) => { - const daysRemaining = getDaysBetween(validFrom, validTo); - if (new Date(validTo).getTime() < new Date(validFrom).getTime()) { - return -daysRemaining; - } - return daysRemaining; -}; -exports.getDaysRemaining = getDaysRemaining; - /** * Fix certificate info for display * @param {object} info The chain obtained from getPeerCertificate() @@ -440,7 +414,7 @@ const parseCertificateInfo = function (info) { } link.validTo = new Date(link.valid_to); link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", "); - link.daysRemaining = getDaysRemaining(new Date(), link.validTo); + link.daysRemaining = dayjs.utc(link.validTo).diff(dayjs.utc(), "day"); existingList[link.fingerprint] = true; diff --git a/test/backend-test/test-domain.js b/test/backend-test/test-domain.js index e7b06efc5..e1c95cd5f 100644 --- a/test/backend-test/test-domain.js +++ b/test/backend-test/test-domain.js @@ -9,6 +9,8 @@ const { R } = require("redbean-node"); const { Notification } = require("../../server/notification"); const { Settings } = require("../../server/settings"); const { setSetting } = require("../../server/util-server"); +const dayjs = require("dayjs"); +dayjs.extend(require("dayjs/plugin/utc")); const testDb = new TestDB(); @@ -231,7 +233,7 @@ describe("Domain Expiry", () => { test("checkExpiry() caches expiration date in database", async () => { await DomainExpiry.checkExpiry("google.com"); // RDAP -> Cache const domain = await DomainExpiry.findByName("google.com"); - assert(Date.now() - domain.lastCheck < 5 * 1000); + assert(dayjs.utc().diff(dayjs.utc(domain.lastCheck), "second") < 5); }); test("sendNotifications() triggers notification for expiring domain", async () => { @@ -240,7 +242,8 @@ describe("Domain Expiry", () => { port: 3010, url: "capture", }; - await setSetting("domainExpiryNotifyDays", [1, 2, 1500], "general"); + const manyDays = 3650; + await setSetting("domainExpiryNotifyDays", [manyDays], "general"); const notif = R.convertToBean("notification", { config: JSON.stringify({ type: "webhook", @@ -252,8 +255,6 @@ describe("Domain Expiry", () => { user_id: 1, name: "Testhook", }); - const manyDays = 3650; - setSetting("domainExpiryNotifyDays", [manyDays], "general"); const [, data] = await Promise.all([ DomainExpiry.sendNotifications("google.com", [notif]), mockWebhook(hook.port, hook.url), diff --git a/test/backend-test/test-util.js b/test/backend-test/test-util.js index a034df11c..ad46c1a84 100644 --- a/test/backend-test/test-util.js +++ b/test/backend-test/test-util.js @@ -2,33 +2,12 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const dayjs = require("dayjs"); -const { getDaysRemaining, getDaysBetween } = require("../../server/util-server"); const { SQL_DATETIME_FORMAT } = require("../../src/util"); dayjs.extend(require("dayjs/plugin/utc")); dayjs.extend(require("dayjs/plugin/customParseFormat")); describe("Server Utilities", () => { - test("getDaysBetween() calculates days between dates within same month", () => { - const days = getDaysBetween(new Date(2025, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 3); - }); - - test("getDaysBetween() calculates days between dates across years", () => { - const days = getDaysBetween(new Date(2024, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 368); - }); - - test("getDaysRemaining() returns positive value when target date is in future", () => { - const days = getDaysRemaining(new Date(2025, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 3); - }); - - test("getDaysRemaining() returns negative value when target date is in past", () => { - const days = getDaysRemaining(new Date(2025, 9, 10), new Date(2025, 9, 7)); - assert.strictEqual(days, -3); - }); - test("SQL_DATETIME_FORMAT constant matches MariaDB/MySQL format", () => { assert.strictEqual(SQL_DATETIME_FORMAT, "YYYY-MM-DD HH:mm:ss"); });