register
gitlabGrab your own sweet-looking '.is-a.dev' subdomain.
clone
https://gitlab.com/bitzzdev/register.gitdeveloperdnsfree-domainis-a-devsubdomain
import t from "ava";import fs from "fs-extra";import path from "path";import internalDomains from "../util/internal.json" with { type: "json" };import reservedDomains from "../util/reserved.json" with { type: "json" };const ignoredRootJSONFiles = ["package-lock.json", "package.json"];const requiredFields = {owner: "object",records: "object"};const optionalFields = {proxied: "boolean",redirect_config: "object"};const requiredOwnerFields = {username: "string"};const optionalOwnerFields = {email: "string"};const optionalRedirectConfigFields = {custom_paths: "object",redirect_paths: "boolean"};const blockedFields = ["domain", "internal", "proxy", "reserved", "services", "subdomain", "nested", "record"];const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;const hostnameRegex = /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/;const domainsPath = path.resolve("domains");const files = fs.readdirSync(domainsPath);function findDuplicateKeys(jsonString) {const duplicateKeys = new Set();const keyStack = [];const keyRegex = /"(.*?)"\s*:/g;let i = 0;while (i < jsonString.length) {const char = jsonString[i];if (char === "{") {keyStack.push({});i++;continue;}if (char === "}") {keyStack.pop();i++;continue;}keyRegex.lastIndex = i;const match = keyRegex.exec(jsonString);if (match && match.index === i && keyStack.length > 0) {const key = match[1];const currentScope = keyStack[keyStack.length - 1];if (currentScope[key]) {duplicateKeys.add(key);} else {currentScope[key] = true;}i = keyRegex.lastIndex;} else {i++;}}return [...duplicateKeys];}async function validateFields(t, obj, fields, file, prefix = "") {for (const key of Object.keys(fields)) {const fieldPath = prefix ? `${prefix}.${key}` : key;if (obj.hasOwnProperty(key)) {t.is(typeof obj[key], fields[key], `${file}: Field ${fieldPath} should be of type ${fields[key]}`);} else if (fields === requiredFields || fields === requiredOwnerFields) {t.true(false, `${file}: Missing required field: ${fieldPath}`);}}}async function validateFileName(t, file) {t.true(file.endsWith(".json"), `${file}: File does not have .json extension`);t.false(file.includes(".is-a.dev"), `${file}: File name should not contain .is-a.dev`);t.true(file === file.toLowerCase(), `${file}: File name should be all lowercase`);t.false(file.includes("--"), `${file}: File name should not contain consecutive hyphens`);const subdomain = file.replace(/\.json$/, "");t.regex(subdomain + ".is-a.dev",hostnameRegex,`${file}: FQDN must be 1-253 characters, can use letters, numbers, dots, and non-consecutive hyphens.`);t.false(internalDomains.includes(subdomain), `${file}: Subdomain name is registered internally`);t.false(reservedDomains.includes(subdomain), `${file}: Subdomain name is reserved`);t.true(!internalDomains.some((i) => subdomain.endsWith(`.${i}`)),`${file}: Subdomain name is registered internally`);t.true(!reservedDomains.some((r) => subdomain.endsWith(`.${r}`)), `${file}: Subdomain name is reserved`);const rootSubdomain = subdomain.split(".").pop();t.false(rootSubdomain.startsWith("_"), `${file}: Root subdomains should not start with an underscore`);}async function processFile(file, t) {const filePath = path.join(domainsPath, file);const data = await fs.readJson(filePath);validateFileName(t, file);// Check for duplicate keysconst rawData = await fs.readFile(filePath, "utf8");const duplicateKeys = findDuplicateKeys(rawData);t.true(!duplicateKeys.length, `${file}: Duplicate keys found: ${duplicateKeys.join(", ")}`);// Validate fieldsvalidateFields(t, data, requiredFields, file);validateFields(t, data.owner, requiredOwnerFields, file, "owner");validateFields(t, data.owner, optionalOwnerFields, file, "owner");validateFields(t, data, optionalFields, file);if (data.owner.email) {t.regex(data.owner.email, emailRegex, `${file}: Owner email should be a valid email address`);t.false(data.owner.email.endsWith("@users.noreply.github.com"),`${file}: Owner email should not be a GitHub no-reply email`);}t.true(Object.keys(data.records).length > 0, `${file}: Missing DNS records`);if (data.redirect_config) {validateFields(t, data.redirect_config, optionalRedirectConfigFields, file, "redirect_config");}for (const field of blockedFields) {t.true(!data.hasOwnProperty(field), `${file}: Disallowed field: ${field}`);}}t("JSON files should not be in the root directory", (t) => {const rootFiles = fs.readdirSync(path.resolve()).filter((file) => file.endsWith(".json") && !ignoredRootJSONFiles.includes(file));t.is(rootFiles.length, 0, "JSON files should not be in the root directory");});t("All files should be valid JSON", async (t) => {await Promise.all(files.map((file) => {return t.notThrows(() => fs.readJson(path.join(domainsPath, file)), `${file}: Invalid JSON file`);}));});t("All files should have valid file names", async (t) => {await Promise.all(files.map((file) => validateFileName(t, file)));});t("All files should have valid required and optional fields", async (t) => {await Promise.all(files.map((file) => processFile(file, t)));});