mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import DatabaseProperty from "./Database/DatabaseProperty";
|
|
import BadDataException from "./Exception/BadDataException";
|
|
import { JSONObject, ObjectType } from "./JSON";
|
|
import { FindOperator } from "typeorm/find-options/FindOperator";
|
|
import Zod, { ZodSchema } from "../Utils/Schema/Zod";
|
|
|
|
export default class Domain extends DatabaseProperty {
|
|
/*
|
|
* Reserved TLDs for testing and documentation (per IANA)
|
|
* These domains can never have real DNS records, so they're safe for testing
|
|
*/
|
|
public static readonly TEST_DOMAIN_SUFFIXES: string[] = [
|
|
".example.com",
|
|
".example.org",
|
|
".example.net",
|
|
".test",
|
|
];
|
|
|
|
/**
|
|
* Checks if a domain is a test/reserved domain that can skip DNS verification.
|
|
* Test domains include .example.com, .example.org, .example.net, and .test TLDs.
|
|
* These are reserved by IANA for documentation and testing purposes.
|
|
*/
|
|
public static isTestDomain(domain: string): boolean {
|
|
const domainLower: string = domain.toLowerCase().trim();
|
|
return Domain.TEST_DOMAIN_SUFFIXES.some((suffix: string) => {
|
|
return domainLower.endsWith(suffix);
|
|
});
|
|
}
|
|
|
|
private _domain: string = "";
|
|
public get domain(): string {
|
|
return this._domain;
|
|
}
|
|
public set domain(v: string) {
|
|
const isValid: boolean = Domain.isValidDomain(v);
|
|
if (!isValid) {
|
|
throw new BadDataException("Domain " + v + " is not in valid format.");
|
|
}
|
|
this._domain = v;
|
|
}
|
|
|
|
public static isValidDomain(domain: string): boolean {
|
|
/*
|
|
* Regex-based domain validation
|
|
* - Each label (part between dots) must be 1-63 characters
|
|
* - Labels can contain alphanumeric characters and hyphens
|
|
* - Labels cannot start or end with a hyphen
|
|
* - TLD must be at least 2 characters and contain only letters
|
|
* - Total length should not exceed 253 characters
|
|
*/
|
|
|
|
if (!domain || domain.length > 253) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Domain validation regex:
|
|
* ^ - start of string
|
|
* (?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+ - one or more labels followed by dot
|
|
* [a-zA-Z]{2,63} - TLD: 2-63 letters only
|
|
* $ - end of string
|
|
*/
|
|
const domainRegex: RegExp =
|
|
/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
|
|
|
|
return domainRegex.test(domain);
|
|
}
|
|
|
|
public constructor(domain: string) {
|
|
super();
|
|
this.domain = domain;
|
|
}
|
|
|
|
public override toString(): string {
|
|
return this.domain;
|
|
}
|
|
|
|
protected static override toDatabase(
|
|
value: Domain | FindOperator<Domain>,
|
|
): string | null {
|
|
if (value) {
|
|
if (typeof value === "string") {
|
|
value = new Domain(value);
|
|
}
|
|
|
|
return value.toString();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override toJSON(): JSONObject {
|
|
return {
|
|
_type: ObjectType.Domain,
|
|
value: (this as Domain).toString(),
|
|
};
|
|
}
|
|
|
|
public static override fromJSON(json: JSONObject): Domain {
|
|
if (json["_type"] === ObjectType.Domain) {
|
|
return new Domain((json["value"] as string) || "");
|
|
}
|
|
|
|
throw new BadDataException("Invalid JSON: " + JSON.stringify(json));
|
|
}
|
|
|
|
protected static override fromDatabase(_value: string): Domain | null {
|
|
if (_value) {
|
|
return new Domain(_value);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static override getSchema(): ZodSchema {
|
|
return Zod.object({
|
|
_type: Zod.literal(ObjectType.Domain),
|
|
value: Zod.string().openapi({
|
|
type: "string",
|
|
example: "example.com",
|
|
}),
|
|
}).openapi({
|
|
type: "object",
|
|
description: "Domain object",
|
|
example: { _type: ObjectType.Domain, value: "example.com" },
|
|
});
|
|
}
|
|
}
|