const { parse: parseTld } = require("tldts"); /* * TODO: * This migration file is scary, because the json file is dynamically updated. * Problem 1: Migration files should ideally be stateless. * Problem 2: This migration only runs once, what happens if rdp-dns.json is updated after this migration has run? * Have to investigate later. */ const rdapDnsData = require("../../extra/rdap-dns.json"); 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": "grpc_url", dns: "hostname", smtp: "hostname", snmp: "hostname", gamedig: "hostname", steam: "hostname", mqtt: "hostname", radius: "hostname", "tailscale-ping": "hostname", "sip-options": "hostname", }; /** * Build set of root TLDs that have RDAP support * @returns {Set} Set of supported root TLDs */ function getSupportedTlds() { const supported = new Set(); const services = rdapDnsData["services"] ?? []; for (const [tlds] of services) { for (const tld of tlds) { supported.add(tld); } } return supported; } /** * Check if a target URL/hostname has RDAP support * @param {string} target URL or hostname * @param {Set} supportedTlds Set of supported root TLDs * @returns {boolean} Whether the target's TLD has RDAP support */ function hasRdapSupport(target, supportedTlds) { if (!target || typeof target !== "string") { return false; } const tld = parseTld(target); if (!tld.publicSuffix || !tld.isIcann) { return false; } const rootTld = tld.publicSuffix.split(".").pop(); return supportedTlds.has(rootTld); } exports.up = async function (knex) { const supportedTlds = getSupportedTlds(); const monitors = await knex("monitor") .where("domain_expiry_notification", 1) .select("id", "type", "url", "hostname", "grpc_url"); const idsToDisable = []; for (const monitor of monitors) { const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type]; if (!targetField || !hasRdapSupport(monitor[targetField], supportedTlds)) { idsToDisable.push(monitor.id); } } if (idsToDisable.length > 0) { await knex("monitor").whereIn("id", idsToDisable).update("domain_expiry_notification", 0); } await knex.schema.alterTable("monitor", function (table) { table.boolean("domain_expiry_notification").defaultTo(0).alter(); }); }; exports.down = async function (knex) { await knex.schema.alterTable("monitor", function (table) { table.boolean("domain_expiry_notification").defaultTo(1).alter(); }); };