fix: Proper processing of date fields (Domain Expiry) with cleanup of unnecessary Date comparison functions (#6638)

Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Shaan
2026-01-09 09:18:17 +06:00
committed by GitHub
parent 0f61d7ee1b
commit a117add908
4 changed files with 20 additions and 65 deletions

View File

@@ -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<DomainExpiry>} 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`);

View File

@@ -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;

View File

@@ -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),

View File

@@ -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");
});