Repository: louislam/uptime-kuma
File: server/model/domain_expiry.js (JavaScript)

Code before:
     * @returns {(null|object)} parsed domain tld
     */
    get tld() {
        return this.parseName().publicSuffix;
    }

    /**
     * @param {Monitor} monitor Monitor object
     * @throws {TranslatableError} Throws an error if the monitor type is unsupported or missing target.
     * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info
     */
    static async checkSupport(monitor) {
        if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {
            throw new TranslatableError("domain_expiry_unsupported_monitor_type");
        }
        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");
        }

        const tld = parseTld(target);

        // Avoid logging for incomplete/invalid input while editing monitors.
        if (!tld.isIcann) {
            throw new TranslatableError("domain_expiry_unsupported_is_icann", { publicSuffix: tld.hostname });
        }
        if (tld.isIp) {
            throw new TranslatableError("domain_expiry_unsupported_is_ip", { hostname: tld.hostname });
        }

        // No one-letter public suffix exists; treat this as an incomplete/invalid input while typing.
        if (tld.publicSuffix.length < 2) {
            throw new TranslatableError("domain_expiry_public_suffix_too_short", { publicSuffix: tld.publicSuffix });
        }

        const rdap = await getRdapServer(tld.publicSuffix);
        if (!rdap) {
            throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", {
                publicSuffix: tld.publicSuffix,
            });
        }

        return {
            domain: tld.domain,
            tld: tld.publicSuffix,
        };
    }

    /**
     * @param {string} domainName Domain name

Review comment (suggestion):
If we do this, it needs to be moved down.
The more specific errors are otherwise ignored.

We should check in the following order instead:
- `isIP`
- `isPrivate`
- `isIcann`

If this is not done this way, this makes our error messages less actionable than it needs to be.

I don't know if missing `domain` or `publicSuffix` should be an error still before the `isIcann` check.



Code after:
     * @returns {(null|object)} parsed domain tld
     */
    get tld() {
        return this.parseName().publicSuffix;
    }

    /**
     * @param {Monitor} monitor Monitor object
     * @throws {TranslatableError} Throws an error if the monitor type is unsupported or missing target.
     * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info
     */
    static async checkSupport(monitor) {
        if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {
            throw new TranslatableError("domain_expiry_unsupported_monitor_type");
        }
        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");
        }

        const tld = parseTld(target);

        // Avoid logging for incomplete/invalid input while editing monitors.
        if (tld.isIp) {
            throw new TranslatableError("domain_expiry_unsupported_is_ip", { hostname: tld.hostname });
        }
        // No one-letter public suffix exists; treat this as an incomplete/invalid input while typing.
        if (tld.publicSuffix.length < 2) {
            throw new TranslatableError("domain_expiry_public_suffix_too_short", { publicSuffix: tld.publicSuffix });
        }
        if (!tld.isIcann) {
            throw new TranslatableError("domain_expiry_unsupported_is_icann", {
                domain: tld.domain,
                publicSuffix: tld.publicSuffix,
            });
        }

        const rdap = await getRdapServer(tld.publicSuffix);
        if (!rdap) {
            throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", {
                publicSuffix: tld.publicSuffix,
            });
        }

        return {
            domain: tld.domain,
            tld: tld.publicSuffix,
        };
    }