From 79b3274441f9f6fe3aea0a1dd82db15a396edbdb Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 8 Jan 2026 11:01:33 +0100 Subject: [PATCH] chore: remove domain expiry from systemd and some other monitors by allowlisting insted of denylisting (#6643) --- .github/copilot-instructions.md | 2 +- server/model/domain_expiry.js | 25 ++++++------------------- src/pages/Details.vue | 1 - src/pages/EditMonitor.vue | 11 ++--------- src/util.js | 21 ++++++++++++++++++++- src/util.ts | 21 +++++++++++++++++++++ 6 files changed, 50 insertions(+), 31 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f29b9f4b3..0a905e0fd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -129,7 +129,7 @@ npm run dev # Starts frontend (port 3000) and backend (port 3001) ## Database -- Primary: SQLite (also supports MariaDB/MySQL/PostgreSQL) +- Primary: SQLite (also supports MariaDB/MySQL) - Migrations in `db/knex_migrations/` using Knex.js - Filename format validated by CI: `node ./extra/check-knex-filenames.mjs` diff --git a/server/model/domain_expiry.js b/server/model/domain_expiry.js index 4c0ee7d46..dfd350313 100644 --- a/server/model/domain_expiry.js +++ b/server/model/domain_expiry.js @@ -1,17 +1,12 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const { R } = require("redbean-node"); -const { log } = require("../../src/util"); +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 { Notification } = require("../notification"); const { default: NodeFetchCache, MemoryCache } = require("node-fetch-cache"); const TranslatableError = require("../translatable-error"); -const TABLE = "domain_expiry"; -// NOTE: Keep these type filters in sync with `showDomainExpiryNotification` in `src/pages/EditMonitor.vue`. -const urlTypes = [ "websocket-upgrade", "http", "keyword", "json-query", "real-browser" ]; -const excludeTypes = [ "docker", "group", "push", "manual", "rabbitmq", "redis" ]; - const cachedFetch = process.env.NODE_ENV ? NodeFetchCache.create({ // cache for 8h cache: new MemoryCache({ ttl: 1000 * 60 * 60 * 8 }) @@ -113,7 +108,7 @@ class DomainExpiry extends BeanModel { * @returns {Promise} Domain bean */ static async findByName(domain) { - return R.findOne(TABLE, "domain = ?", [ domain ]); + return R.findOne("domain_expiry", "domain = ?", [ domain ]); } /** @@ -121,7 +116,7 @@ class DomainExpiry extends BeanModel { * @returns {DomainExpiry} Domain bean */ static createByName(domain) { - const d = R.dispense(TABLE); + const d = R.dispense("domain_expiry"); d.domain = domain; return d; } @@ -149,19 +144,11 @@ class DomainExpiry extends BeanModel { * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info */ static async checkSupport(monitor) { - if (excludeTypes.includes(monitor.type) || monitor.type?.match(/sql$/)) { + if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) { throw new TranslatableError("domain_expiry_unsupported_monitor_type"); } - - let target; - if (urlTypes.includes(monitor.type)) { - target = monitor.url; - } else if (monitor.type === "grpc-keyword") { - target = monitor.grpcUrl; - } else { - target = monitor.hostname; - } - + const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type]; + const target = monitor[targetField]; if (typeof target !== "string" || target.length === 0) { throw new TranslatableError("domain_expiry_unsupported_missing_target"); } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index e541e23b5..5b1639d06 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -29,7 +29,6 @@ monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || - monitor.type === 'mp-health' || monitor.type === 'real-browser' || monitor.type === 'websocket-upgrade' " diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index ed234fac8..e40cf9bc1 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -1356,6 +1356,7 @@ import { MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep, + TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD, } from "../util.ts"; import { timeDurationFormatter } from "../util-frontend"; import isFQDN from "validator/lib/isFQDN"; @@ -1521,15 +1522,7 @@ export default { }, showDomainExpiryNotification() { - // NOTE: Keep this list in sync with `excludeTypes` in `server/model/domain_expiry.js`. - const excludedTypes = [ "docker", "group", "push", "manual", "rabbitmq", "redis" ]; - const type = this.monitor.type; - - if (!type) { - return false; - } - - return !excludedTypes.includes(type) && !type.match(/sql$/); + return this.monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD; }, pageName() { diff --git a/src/util.js b/src/util.js index 3ab9d6294..ee9fc6e66 100644 --- a/src/util.js +++ b/src/util.js @@ -11,7 +11,7 @@ var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_MAX = exports.PING_PER_REQUEST_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_GLOBAL_TIMEOUT_DEFAULT = exports.PING_GLOBAL_TIMEOUT_MAX = exports.PING_GLOBAL_TIMEOUT_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; -exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0; +exports.TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0; const dayjs_1 = require("dayjs"); const jsonata = require("jsonata"); exports.isDev = process.env.NODE_ENV === "development"; @@ -441,3 +441,22 @@ async function evaluateJsonQuery(data, jsonPath, jsonPathOperator, expectedValue } } exports.evaluateJsonQuery = evaluateJsonQuery; +exports.TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = { + 'http': 'url', + 'keyword': 'url', + 'json-query': 'url', + 'real-browser': 'url', + 'websocket-upgrade': 'url', + 'port': 'hostname', + 'ping': 'hostname', + 'grpc-keyword': 'grpcUrl', + 'dns': 'hostname', + 'smtp': 'hostname', + 'snmp': 'hostname', + 'gamedig': 'hostname', + 'steam': 'hostname', + 'mqtt': 'hostname', + 'radius': 'hostname', + 'tailscale-ping': 'hostname', + 'sip-options': 'hostname' +}; diff --git a/src/util.ts b/src/util.ts index 4fd1cce9d..5e2cf466e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -722,3 +722,24 @@ export async function evaluateJsonQuery(data: any, jsonPath: string, jsonPathOpe throw new Error(`Error evaluating JSON query: ${err.message}. Response from server was: ${response}`); } } + +// these types will have domain expiry support via the specified field +export const TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = { + 'http': 'url', + 'keyword': 'url', + 'json-query': 'url', + 'real-browser': 'url', + 'websocket-upgrade': 'url', + 'port': 'hostname', + 'ping': 'hostname', + 'grpc-keyword': 'grpcUrl', + 'dns': 'hostname', + 'smtp': 'hostname', + 'snmp': 'hostname', + 'gamedig': 'hostname', + 'steam': 'hostname', + 'mqtt': 'hostname', + 'radius': 'hostname', + 'tailscale-ping': 'hostname', + 'sip-options': 'hostname' +} as const;