From 51639353409df960edfe5945944d5a96cc299918 Mon Sep 17 00:00:00 2001 From: William Harrison Date: Mon, 29 Jul 2024 08:34:45 +0800 Subject: [PATCH 1/2] 1 letter subdomains + prettier --- scripts/action-utils.js | 10 +- scripts/register-domains.js | 95 +++-- scripts/reply.js | 22 +- tests/cpanel.test.js | 365 +++++++++--------- tests/domain-service.test.js | 705 +++++++++++++++++++---------------- tests/domain-utils.test.js | 13 +- tests/domains.test.js | 62 +-- tests/register.test.js | 417 +++++++++++---------- tests/validations.test.js | 274 ++++++++------ utils/constants.js | 42 +-- utils/domain-service.js | 283 ++++++++------ utils/get-domain.js | 35 +- utils/helpers.js | 74 ++-- utils/invalid-domains.json | 44 +-- utils/lib/cpanel.js | 197 +++++----- utils/validations.js | 195 +++++----- 16 files changed, 1552 insertions(+), 1281 deletions(-) diff --git a/scripts/action-utils.js b/scripts/action-utils.js index f4fde5761..38a33a420 100644 --- a/scripts/action-utils.js +++ b/scripts/action-utils.js @@ -1,8 +1,8 @@ module.exports = { - hasLabel: (context, label) => { - const pr = context.payload.pull_request || context.payload.issue; - const { labels = [] } = pr; + hasLabel: (context, label) => { + const pr = context.payload.pull_request || context.payload.issue; + const { labels = [] } = pr; - return !!labels.find(({ name }) => name === label); - }, + return !!labels.find(({ name }) => name === label); + }, }; diff --git a/scripts/register-domains.js b/scripts/register-domains.js index 20670f8af..94bc02089 100644 --- a/scripts/register-domains.js +++ b/scripts/register-domains.js @@ -1,61 +1,76 @@ -const R = require('ramda'); -const { VALID_RECORD_TYPES, DOMAIN_HOST_IP, TTL, ENV } = require('../utils/constants'); -const { domainService: dc } = require('../utils/domain-service'); -const { getDomains: gd } = require('../utils/get-domain'); +const R = require("ramda"); +const { + VALID_RECORD_TYPES, + DOMAIN_HOST_IP, + TTL, + ENV, +} = require("../utils/constants"); +const { domainService: dc } = require("../utils/domain-service"); +const { getDomains: gd } = require("../utils/get-domain"); const getRecords = R.compose(R.toPairs, R.pick(VALID_RECORD_TYPES)); const address = (type, value) => { - if ('URL' === type) return `${value}`.replace(/\/$/g, ''); - if ('TXT' === type) return value; - return (type === 'CNAME' ? `${value}`.toLowerCase() : `${value}`).replace(/[/.]$/g, ''); + if ("URL" === type) return `${value}`.replace(/\/$/g, ""); + if ("TXT" === type) return value; + return (type === "CNAME" ? `${value}`.toLowerCase() : `${value}`).replace( + /[/.]$/g, + "", + ); }; -const toHostList = R.chain(data => { - // URL redirection must contain explicit A record - // Wildcard A record breaks when used with MX - // Ref: https://github.com/is-a-dev/register/issues/2365 - if ((data.record.URL && data.record.MX) || data.name === '@') { - data.record.A = [ DOMAIN_HOST_IP ] - } +const toHostList = R.chain((data) => { + // URL redirection must contain explicit A record + // Wildcard A record breaks when used with MX + // Ref: https://github.com/is-a-dev/register/issues/2365 + if ((data.record.URL && data.record.MX) || data.name === "@") { + data.record.A = [DOMAIN_HOST_IP]; + } - const records = getRecords(data.record); + const records = getRecords(data.record); - return R.chain(([recordType, values]) => { - const valueList = Array.isArray(values) ? values : [values]; + return R.chain(([recordType, values]) => { + const valueList = Array.isArray(values) ? values : [values]; - return valueList.map((value, index) => ({ - name: data.name, - type: recordType, - address: address(recordType, value), - ttl: TTL, - ...(recordType === 'MX' ? { priority: index + 20 } : {}) - })) - }, records) + return valueList.map((value, index) => ({ + name: data.name, + type: recordType, + address: address(recordType, value), + ttl: TTL, + ...(recordType === "MX" ? { priority: index + 20 } : {}), + })); + }, records); }); -const registerDomains = async ({ domainService, getDomains, log = () => { } }) => { - const domains = await getDomains().then(toHostList); +const registerDomains = async ({ + domainService, + getDomains, + log = () => {}, +}) => { + const domains = await getDomains().then(toHostList); - if (domains.length === 0) - return Promise.reject(new Error('Nothing to register')); + if (domains.length === 0) + return Promise.reject(new Error("Nothing to register")); - log(`${domains.length} records found`); - return domainService.updateHosts(domains); + log(`${domains.length} records found`); + return domainService.updateHosts(domains); }; const main = async () => { - console.log(`Running in ${ENV} mode`); - const result = await registerDomains({ domainService: dc, getDomains: gd, log: console.log }); - console.log(result); + console.log(`Running in ${ENV} mode`); + const result = await registerDomains({ + domainService: dc, + getDomains: gd, + log: console.log, + }); + console.log(result); }; if (require.main === module) { - main().catch(e => { - console.error(e); - process.exit(1); - }); + main().catch((e) => { + console.error(e); + process.exit(1); + }); } else { - module.exports = { toHostList, registerDomains }; + module.exports = { toHostList, registerDomains }; } - diff --git a/scripts/reply.js b/scripts/reply.js index 233f56ed4..dd4df142d 100644 --- a/scripts/reply.js +++ b/scripts/reply.js @@ -1,4 +1,3 @@ - const getInstructions = () => ` The changes have been published!! It should reflect in less than 24 hours. @@ -35,16 +34,15 @@ Help me in my mission to keep this service alive forever by donating! `; module.exports = { - async instructions(context, github) { - const pr = context.payload.issue || context.payload.pull_request; - const { number } = pr; + async instructions(context, github) { + const pr = context.payload.issue || context.payload.pull_request; + const { number } = pr; - await github.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: number, - body: getInstructions(), - }); - } + await github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: number, + body: getInstructions(), + }); + }, }; - diff --git a/tests/cpanel.test.js b/tests/cpanel.test.js index 162a9a5d3..b2415f8b4 100644 --- a/tests/cpanel.test.js +++ b/tests/cpanel.test.js @@ -1,195 +1,210 @@ -const R = require('ramda'); -const { CpanelClient } = require('../utils/lib/cpanel'); +const R = require("ramda"); +const { CpanelClient } = require("../utils/lib/cpanel"); -const mockFetch = (expectRequest, decorate = R.identity) => (reqUrl, request) => { - expectRequest(reqUrl, request); - return Promise.resolve({ - json: async () => decorate(request), - }); -}; - -describe('Cpanel client', () => { - describe('fetchzonerecords', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=0&domain=a.b&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, +const mockFetch = + (expectRequest, decorate = R.identity) => + (reqUrl, request) => { + expectRequest(reqUrl, request); + return Promise.resolve({ + json: async () => decorate(request), }); - }); + }; - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); +describe("Cpanel client", () => { + describe("fetchzonerecords", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/json-api/cpanel?customonly=0&domain=a.b&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); - await cpanel.zone.fetch(); + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.zone.fetch(); + }); + + it("should make the correct request with query", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/json-api/cpanel?customonly=0&domain=foobar.boeey&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); + + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.zone.fetch({ domain: "foobar.boeey" }); + }); }); - it('should make the correct request with query', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=0&domain=foobar.boeey&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, + describe("addzonerecord", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/json-api/cpanel?domain=a.b&name=googo&type=CNAME&cname=beey&ttl=2020&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); + + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.zone.add({ + name: "googo", + type: "boyee", + cname: "beey", + type: "CNAME", + ttl: 2020, + }); }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.zone.fetch({ domain: 'foobar.boeey' }); }); - }); - describe('addzonerecord', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?domain=a.b&name=googo&type=CNAME&cname=beey&ttl=2020&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, + describe("addzonerecord", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/json-api/cpanel?domain=a.b&line=500&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); + + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.zone.remove({ + line: 500, + }); }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.zone.add({ - name: 'googo', - type: 'boyee', - cname: 'beey', - type: 'CNAME', - ttl: 2020, - }); }); - }); - describe('addzonerecord', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?domain=a.b&line=500&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, + describe("fetchredirections", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/execute/Mime/list_redirects?cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=list_redirects&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); + + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.redirection.fetch(); }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.zone.remove({ - line: 500, - }); }); - }); + describe("addredirection", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/execute/Mime/add_redirect?domain=googo&destination=https%3A%2F%2Foodf.com&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=add_redirect&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); - describe('fetchredirections', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/execute/Mime/list_redirects?cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=list_redirects&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.redirection.add({ + domain: "googo", + destination: "https://oodf.com", + }); }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.redirection.fetch(); }); - }); - describe('addredirection', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/execute/Mime/add_redirect?domain=googo&destination=https%3A%2F%2Foodf.com&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=add_redirect&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, + + describe("deleteredirection", () => { + it("should make the correct request", async () => { + const fetch = mockFetch((url, request) => { + expect(url).toBe( + "https://example.com:2000/execute/Mime/delete_redirect?domain=googo&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=delete_redirect&cpanel_jsonapi_apiversion=2", + ); + expect(request).toEqual({ + headers: { + Authorization: "cpanel boy:boybyebye", + }, + rejectUnauthorized: false, + }); + }); + + const cpanel = CpanelClient({ + host: "example.com", + port: 2000, + username: "boy", + apiKey: "boybyebye", + domain: "a.b", + dependencies: { fetch }, + }); + + await cpanel.redirection.remove({ domain: "googo" }); }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.redirection.add({ - domain: 'googo', - destination: 'https://oodf.com' - }); }); - }); - - describe('deleteredirection', () => { - it('should make the correct request', async () => { - const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/execute/Mime/delete_redirect?domain=googo&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=Mime&cpanel_jsonapi_func=delete_redirect&cpanel_jsonapi_apiversion=2'); - expect(request).toEqual({ - headers: { - Authorization: 'cpanel boy:boybyebye', - }, - rejectUnauthorized: false, - }); - }); - - const cpanel = CpanelClient({ - host: 'example.com', - port: 2000, - username: 'boy', - apiKey: 'boybyebye', - domain: 'a.b', - dependencies: { fetch }, - }); - - await cpanel.redirection.remove({ domain: 'googo' }); - }); - }); }); - diff --git a/tests/domain-service.test.js b/tests/domain-service.test.js index 077aa4ea2..dfd6400ed 100644 --- a/tests/domain-service.test.js +++ b/tests/domain-service.test.js @@ -1,344 +1,421 @@ -const R = require('ramda'); -const { getDomainService, diffRecords } = require('../utils/domain-service'); -const { DOMAIN_DOMAIN } = require('../utils/constants'); +const R = require("ramda"); +const { getDomainService, diffRecords } = require("../utils/domain-service"); +const { DOMAIN_DOMAIN } = require("../utils/constants"); -const getCpanel = ({ zone, addZone, removeZone, redir, addRedir, removeRedir, addEmail, removeEmail } = {}) => ({ - zone: { - fetch: (_) => zone(), - add: (rec) => addZone(rec), - remove: (rec) => removeZone(rec), - }, - redirection: { - fetch: (_) => redir(), - add: (rec) => addRedir(rec), - remove: (rec) => removeRedir(rec), - }, - email: { - add: (rec) => addEmail(rec), - remove: (rec) => removeEmail(rec), - }, +const getCpanel = ({ + zone, + addZone, + removeZone, + redir, + addRedir, + removeRedir, + addEmail, + removeEmail, +} = {}) => ({ + zone: { + fetch: (_) => zone(), + add: (rec) => addZone(rec), + remove: (rec) => removeZone(rec), + }, + redirection: { + fetch: (_) => redir(), + add: (rec) => addRedir(rec), + remove: (rec) => removeRedir(rec), + }, + email: { + add: (rec) => addEmail(rec), + remove: (rec) => removeEmail(rec), + }, }); -describe('diffRecords', () => { - it('should show added record', () => { - const oldRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '111.1.1212.1' }, - ]; - const newRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '111.1.1212.1' }, - { name: 'boo', type: 'CNAME', address: 'x.com' }, - ]; +describe("diffRecords", () => { + it("should show added record", () => { + const oldRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "111.1.1212.1" }, + ]; + const newRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "111.1.1212.1" }, + { name: "boo", type: "CNAME", address: "x.com" }, + ]; - const result = diffRecords(oldRecords, newRecords); - expect(result).toEqual({ - remove: [], - add: [ - { name: 'boo', type: 'CNAME', address: 'x.com' }, - ], + const result = diffRecords(oldRecords, newRecords); + expect(result).toEqual({ + remove: [], + add: [{ name: "boo", type: "CNAME", address: "x.com" }], + }); }); - }); - it('should show edited records', () => { - const oldRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '111.1.1212.1' }, - ]; - const newRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '69.69.69.69' }, - ]; + it("should show edited records", () => { + const oldRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "111.1.1212.1" }, + ]; + const newRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "69.69.69.69" }, + ]; - const result = diffRecords(oldRecords, newRecords); - expect(result).toEqual({ - remove: [ - { name: 'xa', type: 'A', address: '111.1.1212.1' }, - ], - add: [ - { name: 'xa', type: 'A', address: '69.69.69.69' }, - ], + const result = diffRecords(oldRecords, newRecords); + expect(result).toEqual({ + remove: [{ name: "xa", type: "A", address: "111.1.1212.1" }], + add: [{ name: "xa", type: "A", address: "69.69.69.69" }], + }); }); - }); - it('should show added records with the same name and record type', () => { - const oldRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '69.69.69.69' }, - ]; - const newRecords = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xa', type: 'A', address: '69.69.69.69' }, - { name: 'xa', type: 'A', address: '69.69.4.20' }, - ]; + it("should show added records with the same name and record type", () => { + const oldRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "69.69.69.69" }, + ]; + const newRecords = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xa", type: "A", address: "69.69.69.69" }, + { name: "xa", type: "A", address: "69.69.4.20" }, + ]; - const result = diffRecords(oldRecords, newRecords); - expect(result).toEqual({ - remove: [], - add: [ - { name: 'xa', type: 'A', address: '69.69.4.20' }, - ], + const result = diffRecords(oldRecords, newRecords); + expect(result).toEqual({ + remove: [], + add: [{ name: "xa", type: "A", address: "69.69.4.20" }], + }); }); - }); - it('should diff complex changes', () => { - const oldRecords = [ - { name: 'a', type: 'CNAME', address: 'fck.com.' }, - { name: 'b', type: 'A', address: '69.69.69.69' }, - { name: '111', type: 'CNAME', address: 'x' }, - { name: 'd', type: 'A', address: '69.69.4.20' }, - ]; - const newRecords = [ - { name: '111', type: 'CNAME', address: 'x' }, - { name: 'd', type: 'CNAME', address: 'duck.com' }, - { name: 'a', type: 'CNAME', address: 'og.com' }, - { name: 'b', type: 'A', address: '69.69.69.69' }, - { name: 'b', type: 'A', address: '69.69.4.20' }, - { name: 'c', type: 'CNAME', address: 'ccc.cc' }, - ]; + it("should diff complex changes", () => { + const oldRecords = [ + { name: "a", type: "CNAME", address: "fck.com." }, + { name: "b", type: "A", address: "69.69.69.69" }, + { name: "111", type: "CNAME", address: "x" }, + { name: "d", type: "A", address: "69.69.4.20" }, + ]; + const newRecords = [ + { name: "111", type: "CNAME", address: "x" }, + { name: "d", type: "CNAME", address: "duck.com" }, + { name: "a", type: "CNAME", address: "og.com" }, + { name: "b", type: "A", address: "69.69.69.69" }, + { name: "b", type: "A", address: "69.69.4.20" }, + { name: "c", type: "CNAME", address: "ccc.cc" }, + ]; - const result = diffRecords(oldRecords, newRecords); - expect(result).toEqual({ - remove: [ - { name: 'a', type: 'CNAME', address: 'fck.com.' }, - { name: 'd', type: 'A', address: '69.69.4.20' }, - ], - add: [ - { name: 'd', type: 'CNAME', address: 'duck.com' }, - { name: 'a', type: 'CNAME', address: 'og.com' }, - { name: 'b', type: 'A', address: '69.69.4.20' }, - { name: 'c', type: 'CNAME', address: 'ccc.cc' }, - ], + const result = diffRecords(oldRecords, newRecords); + expect(result).toEqual({ + remove: [ + { name: "a", type: "CNAME", address: "fck.com." }, + { name: "d", type: "A", address: "69.69.4.20" }, + ], + add: [ + { name: "d", type: "CNAME", address: "duck.com" }, + { name: "a", type: "CNAME", address: "og.com" }, + { name: "b", type: "A", address: "69.69.4.20" }, + { name: "c", type: "CNAME", address: "ccc.cc" }, + ], + }); }); - }); }); -describe('Domain service', () => { - const addZone = jest.fn(async () => ({})); - const removeZone = jest.fn(async () => ({})); - const addRedir = jest.fn(async () => ({})); - const removeRedir = jest.fn(async () => ({})); - const addEmail = jest.fn(async () => ({})); - const removeEmail = jest.fn(async () => ({})); +describe("Domain service", () => { + const addZone = jest.fn(async () => ({})); + const removeZone = jest.fn(async () => ({})); + const addRedir = jest.fn(async () => ({})); + const removeRedir = jest.fn(async () => ({})); + const addEmail = jest.fn(async () => ({})); + const removeEmail = jest.fn(async () => ({})); - const mockDS = ({ zones, redirections }) => getDomainService({ - cpanel: getCpanel({ - zone: async () => zones, - redir: async () => redirections, - addZone, - addEmail, - addRedir, - removeZone, - removeRedir, - removeEmail, - }) - }); + const mockDS = ({ zones, redirections }) => + getDomainService({ + cpanel: getCpanel({ + zone: async () => zones, + redir: async () => redirections, + addZone, + addEmail, + addRedir, + removeZone, + removeRedir, + removeEmail, + }), + }); - const getRecordCalls = recfn => recfn.mock.calls - .map(R.head) - .map(R.pick(['name', 'type', 'address', 'redirect', 'domain', 'line', 'priority', 'exchanger'])); + const getRecordCalls = (recfn) => + recfn.mock.calls + .map(R.head) + .map( + R.pick([ + "name", + "type", + "address", + "redirect", + "domain", + "line", + "priority", + "exchanger", + ]), + ); - beforeEach(() => { - addZone.mockClear(); - removeZone.mockClear(); - addRedir.mockClear(); - removeRedir.mockClear(); - addEmail.mockClear(); - removeEmail.mockClear(); - }); - - describe('getHosts', () => { - it('should resolve with a list of hosts', async () => { - const zones = [ - { name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { name: 'xx', type: 'A', address: '111.1.1212.1' }, - ]; - const redirections = []; - const zone = async () => zones; - const redir = async () => redirections; - const mockDomainService = getDomainService({ cpanel: getCpanel({ zone, redir }) }); - const list = await mockDomainService.getHosts(); - - expect(list).toEqual([ - { name: 'xx', type: 'CNAME', address: 'fck.com' }, - { name: 'xx', type: 'A', address: '111.1.1212.1' }, - ]); + beforeEach(() => { + addZone.mockClear(); + removeZone.mockClear(); + addRedir.mockClear(); + removeRedir.mockClear(); + addEmail.mockClear(); + removeEmail.mockClear(); }); - it('should resolve with a redirections', async () => { - const zones = [ - { line: '111', name: 'xx', type: 'CNAME', address: 'fck.com.' }, - { line: '112', name: 'xx', type: 'A', address: '111.1.1212.1' }, - ]; - const redirections = [ - { domain: 'foo.booboo.xyz', destination: 'https://google.com' }, - { domain: 'foo1.booboo.xyz', destination: 'https://duck.com' }, - ]; - const zone = async () => zones; - const redir = async () => redirections; - const mockDomainService = getDomainService({ cpanel: getCpanel({ zone, redir }) }); - const list = await mockDomainService.getHosts(); + describe("getHosts", () => { + it("should resolve with a list of hosts", async () => { + const zones = [ + { name: "xx", type: "CNAME", address: "fck.com." }, + { name: "xx", type: "A", address: "111.1.1212.1" }, + ]; + const redirections = []; + const zone = async () => zones; + const redir = async () => redirections; + const mockDomainService = getDomainService({ + cpanel: getCpanel({ zone, redir }), + }); + const list = await mockDomainService.getHosts(); - expect(list).toEqual([ - { id: '111', name: 'xx', type: 'CNAME', address: 'fck.com' }, - { id: '112', name: 'xx', type: 'A', address: '111.1.1212.1' }, - { id: `foo.${DOMAIN_DOMAIN}`, name: 'foo', type: 'URL', address: 'https://google.com' }, - { id: `foo1.${DOMAIN_DOMAIN}`, name: 'foo1', type: 'URL', address: 'https://duck.com' }, - ]); - }); - }); + expect(list).toEqual([ + { name: "xx", type: "CNAME", address: "fck.com" }, + { name: "xx", type: "A", address: "111.1.1212.1" }, + ]); + }); - describe('updateHosts', () => { - it('should append new hosts with existing ones and set it', async () => { - const zones = [ - { line: 1, name: 'a', type: 'CNAME', address: 'boo' }, - { line: 2, name: 'b', type: 'CNAME', address: 'goo' }, - ]; - const redirections = []; + it("should resolve with a redirections", async () => { + const zones = [ + { line: "111", name: "xx", type: "CNAME", address: "fck.com." }, + { line: "112", name: "xx", type: "A", address: "111.1.1212.1" }, + ]; + const redirections = [ + { domain: "foo.booboo.xyz", destination: "https://google.com" }, + { domain: "foo1.booboo.xyz", destination: "https://duck.com" }, + ]; + const zone = async () => zones; + const redir = async () => redirections; + const mockDomainService = getDomainService({ + cpanel: getCpanel({ zone, redir }), + }); + const list = await mockDomainService.getHosts(); - const mockDomainService = mockDS({ zones, redirections }); - await mockDomainService.updateHosts([ - { name: 'a', type: 'CNAME', address: 'boo' }, - { name: 'b', type: 'CNAME', address: 'goo' }, - { name: 'c', type: 'A', address: '12.131321.213' }, - { name: 'c', type: 'MX', address: 'foobar.com', priority: 2 }, - ]); - - expect(addZone).toHaveBeenCalledTimes(1); - expect(getRecordCalls(addZone)).toEqual([ - { name: 'c', type: 'A', address: '12.131321.213' }, - ]); - - expect(addEmail).toHaveBeenCalledTimes(1); - expect(getRecordCalls(addEmail)).toEqual([ - { domain: 'c.is-a.dev', exchanger: 'foobar.com', priority: 2 }, - ]); - - expect(removeZone).toHaveBeenCalledTimes(0); - expect(removeEmail).toHaveBeenCalledTimes(0); + expect(list).toEqual([ + { id: "111", name: "xx", type: "CNAME", address: "fck.com" }, + { id: "112", name: "xx", type: "A", address: "111.1.1212.1" }, + { + id: `foo.${DOMAIN_DOMAIN}`, + name: "foo", + type: "URL", + address: "https://google.com", + }, + { + id: `foo1.${DOMAIN_DOMAIN}`, + name: "foo1", + type: "URL", + address: "https://duck.com", + }, + ]); + }); }); - it('should update matching host and set it', async () => { - const zones = [ - { line: 1, name: 'a', type: 'CNAME', address: 'boo' }, - { line: 2, name: 'b', type: 'CNAME', address: 'goo' }, - ]; - const redirections = []; + describe("updateHosts", () => { + it("should append new hosts with existing ones and set it", async () => { + const zones = [ + { line: 1, name: "a", type: "CNAME", address: "boo" }, + { line: 2, name: "b", type: "CNAME", address: "goo" }, + ]; + const redirections = []; - const mockDomainService = mockDS({ zones, redirections }); - await mockDomainService.updateHosts([ - { name: 'a', type: 'CNAME', address: 'boo' }, - { name: 'b', type: 'CNAME', address: 'googoogaga' }, - ]); + const mockDomainService = mockDS({ zones, redirections }); + await mockDomainService.updateHosts([ + { name: "a", type: "CNAME", address: "boo" }, + { name: "b", type: "CNAME", address: "goo" }, + { name: "c", type: "A", address: "12.131321.213" }, + { name: "c", type: "MX", address: "foobar.com", priority: 2 }, + ]); - expect(addZone).toHaveBeenCalledTimes(1); - expect(getRecordCalls(addZone)).toEqual([ - { name: 'b', type: 'CNAME', address: 'googoogaga' }, - ]); - expect(removeZone).toHaveBeenCalledTimes(1); - expect(getRecordCalls(removeZone)).toEqual([ - { line: 2 }, - ]); + expect(addZone).toHaveBeenCalledTimes(1); + expect(getRecordCalls(addZone)).toEqual([ + { name: "c", type: "A", address: "12.131321.213" }, + ]); + + expect(addEmail).toHaveBeenCalledTimes(1); + expect(getRecordCalls(addEmail)).toEqual([ + { domain: "c.is-a.dev", exchanger: "foobar.com", priority: 2 }, + ]); + + expect(removeZone).toHaveBeenCalledTimes(0); + expect(removeEmail).toHaveBeenCalledTimes(0); + }); + + it("should update matching host and set it", async () => { + const zones = [ + { line: 1, name: "a", type: "CNAME", address: "boo" }, + { line: 2, name: "b", type: "CNAME", address: "goo" }, + ]; + const redirections = []; + + const mockDomainService = mockDS({ zones, redirections }); + await mockDomainService.updateHosts([ + { name: "a", type: "CNAME", address: "boo" }, + { name: "b", type: "CNAME", address: "googoogaga" }, + ]); + + expect(addZone).toHaveBeenCalledTimes(1); + expect(getRecordCalls(addZone)).toEqual([ + { name: "b", type: "CNAME", address: "googoogaga" }, + ]); + expect(removeZone).toHaveBeenCalledTimes(1); + expect(getRecordCalls(removeZone)).toEqual([{ line: 2 }]); + }); + + it("should update matching host and set it", async () => { + const zones = [ + { line: 1, name: "a", type: "CNAME", address: "boo" }, + { line: 2, name: "b", type: "CNAME", address: "goo" }, + { line: 3, name: "b", type: "CNAME", address: "xaa" }, + ]; + const redirections = []; + + const mockDomainService = mockDS({ zones, redirections }); + await mockDomainService.updateHosts([ + { name: "a", type: "CNAME", address: "boo" }, + { name: "b", type: "CNAME", address: "googoogaga" }, + { name: "b", type: "CNAME", address: "farboo" }, + ]); + + expect(addZone).toHaveBeenCalledTimes(2); + expect(getRecordCalls(addZone)).toEqual([ + { name: "b", type: "CNAME", address: "googoogaga" }, + { name: "b", type: "CNAME", address: "farboo" }, + ]); + expect(removeZone).toHaveBeenCalledTimes(2); + expect(getRecordCalls(removeZone)).toEqual([ + { line: 3 }, + { line: 2 }, + ]); + }); + + it("should workout this complex example", async () => { + const zones = [ + { line: 1, name: "a", type: "CNAME", address: "world" }, + { line: 2, name: "b", type: "A", address: "1" }, + { line: 3, name: "b", type: "A", address: "2" }, + { line: 4, name: "c", type: "CNAME", address: "hello.com" }, + { + line: 5, + name: "c", + type: "MX", + address: "mx1.hello.com", + priority: 20, + }, + { + line: 6, + name: "c", + type: "MX", + address: "mx2.hello.com", + priority: 21, + }, + { + line: 7, + name: "b", + type: "MX", + address: "foo.bar", + priority: 20, + }, + { line: 101, name: "x", type: "A", address: "1" }, + { line: 99, name: "y", type: "A", address: "2" }, + { line: 100, name: "z", type: "A", address: "3" }, + ]; + const redirections = [ + { + domain: `b.${DOMAIN_DOMAIN}`, + destination: "https://foobar.com", + }, + { + domain: `c.${DOMAIN_DOMAIN}`, + destination: "https://goobar.com", + }, + { + domain: `x.${DOMAIN_DOMAIN}`, + destination: "https://example.com", + }, + ]; + + const mockDomainService = mockDS({ zones, redirections }); + await mockDomainService.updateHosts([ + { name: "a", type: "CNAME", address: "boo" }, + { name: "b", type: "A", address: "1" }, + { name: "b", type: "A", address: "2" }, + { name: "b", type: "A", address: "3" }, + { name: "b", type: "URL", address: "https://wowow.com" }, + { name: "c", type: "CNAME", address: "hello.com" }, + { name: "c", type: "URL", address: "https://goobar.com" }, + { name: "d", type: "CNAME", address: "helo.com" }, + { name: "d", type: "URL", address: "https://hhh.com" }, + { name: "x", type: "URL", address: "https://example69.com" }, + { + name: "c", + type: "MX", + address: "mx2.hello.com", + priority: 21, + }, + { name: "a", type: "MX", address: "example.com", priority: 20 }, + ]); + + expect(addZone).toHaveBeenCalledTimes(3); + expect(getRecordCalls(addZone)).toEqual([ + { name: "a", type: "CNAME", address: "boo" }, + { name: "b", type: "A", address: "3" }, + { name: "d", type: "CNAME", address: "helo.com" }, + ]); + expect(removeZone).toHaveBeenCalledTimes(4); + expect(getRecordCalls(removeZone)).toEqual([ + { line: 101 }, + { line: 100 }, + { line: 99 }, + { line: 1 }, + ]); + + expect(addEmail).toHaveBeenCalledTimes(1); + expect(getRecordCalls(addEmail)).toEqual([ + { + domain: "a.is-a.dev", + exchanger: "example.com", + priority: 20, + }, + ]); + expect(removeEmail).toHaveBeenCalledTimes(2); + expect(getRecordCalls(removeEmail)).toEqual([ + { domain: "b.is-a.dev", exchanger: "foo.bar", priority: 20 }, + { + domain: "c.is-a.dev", + exchanger: "mx1.hello.com", + priority: 20, + }, + ]); + + expect(addRedir).toHaveBeenCalledTimes(3); + expect(getRecordCalls(addRedir)).toEqual([ + { + domain: `b.${DOMAIN_DOMAIN}`, + type: "permanent", + redirect: "https://wowow.com", + }, + { + domain: `d.${DOMAIN_DOMAIN}`, + type: "permanent", + redirect: "https://hhh.com", + }, + { + domain: `x.${DOMAIN_DOMAIN}`, + type: "permanent", + redirect: "https://example69.com", + }, + ]); + expect(removeRedir).toHaveBeenCalledTimes(2); + expect(getRecordCalls(removeRedir)).toEqual([ + { domain: `b.${DOMAIN_DOMAIN}` }, + { domain: `x.${DOMAIN_DOMAIN}` }, + ]); + }); }); - - it('should update matching host and set it', async () => { - const zones = [ - { line: 1, name: 'a', type: 'CNAME', address: 'boo' }, - { line: 2, name: 'b', type: 'CNAME', address: 'goo' }, - { line: 3, name: 'b', type: 'CNAME', address: 'xaa' }, - ]; - const redirections = []; - - const mockDomainService = mockDS({ zones, redirections }); - await mockDomainService.updateHosts([ - { name: 'a', type: 'CNAME', address: 'boo' }, - { name: 'b', type: 'CNAME', address: 'googoogaga' }, - { name: 'b', type: 'CNAME', address: 'farboo' }, - ]); - - expect(addZone).toHaveBeenCalledTimes(2); - expect(getRecordCalls(addZone)).toEqual([ - { name: 'b', type: 'CNAME', address: 'googoogaga' }, - { name: 'b', type: 'CNAME', address: 'farboo' }, - ]); - expect(removeZone).toHaveBeenCalledTimes(2); - expect(getRecordCalls(removeZone)).toEqual([ - { line: 3 }, - { line: 2 }, - ]); - }); - - it('should workout this complex example', async () => { - const zones = [ - { line: 1, name: 'a', type: 'CNAME', address: 'world' }, - { line: 2, name: 'b', type: 'A', address: '1' }, - { line: 3, name: 'b', type: 'A', address: '2' }, - { line: 4, name: 'c', type: 'CNAME', address: 'hello.com' }, - { line: 5, name: 'c', type: 'MX', address: 'mx1.hello.com', priority: 20 }, - { line: 6, name: 'c', type: 'MX', address: 'mx2.hello.com', priority: 21 }, - { line: 7, name: 'b', type: 'MX', address: 'foo.bar', priority: 20 }, - { line: 101, name: 'x', type: 'A', address: '1' }, - { line: 99, name: 'y', type: 'A', address: '2' }, - { line: 100, name: 'z', type: 'A', address: '3' }, - ]; - const redirections = [ - { domain: `b.${DOMAIN_DOMAIN}`, destination: 'https://foobar.com' }, - { domain: `c.${DOMAIN_DOMAIN}`, destination: 'https://goobar.com' }, - { domain: `x.${DOMAIN_DOMAIN}`, destination: 'https://example.com' }, - ]; - - const mockDomainService = mockDS({ zones, redirections }); - await mockDomainService.updateHosts([ - { name: 'a', type: 'CNAME', address: 'boo' }, - { name: 'b', type: 'A', address: '1' }, - { name: 'b', type: 'A', address: '2' }, - { name: 'b', type: 'A', address: '3' }, - { name: 'b', type: 'URL', address: 'https://wowow.com' }, - { name: 'c', type: 'CNAME', address: 'hello.com' }, - { name: 'c', type: 'URL', address: 'https://goobar.com' }, - { name: 'd', type: 'CNAME', address: 'helo.com' }, - { name: 'd', type: 'URL', address: 'https://hhh.com' }, - { name: 'x', type: 'URL', address: 'https://example69.com' }, - { name: 'c', type: 'MX', address: 'mx2.hello.com', priority: 21 }, - { name: 'a', type: 'MX', address: 'example.com', priority: 20 }, - ]); - - expect(addZone).toHaveBeenCalledTimes(3); - expect(getRecordCalls(addZone)).toEqual([ - { name: 'a', type: 'CNAME', address: 'boo' }, - { name: 'b', type: 'A', address: '3' }, - { name: 'd', type: 'CNAME', address: 'helo.com' }, - ]); - expect(removeZone).toHaveBeenCalledTimes(4); - expect(getRecordCalls(removeZone)).toEqual([ - { line: 101 }, - { line: 100 }, - { line: 99 }, - { line: 1 }, - ]); - - expect(addEmail).toHaveBeenCalledTimes(1); - expect(getRecordCalls(addEmail)).toEqual([ - { domain: 'a.is-a.dev', exchanger: 'example.com', priority: 20 }, - ]); - expect(removeEmail).toHaveBeenCalledTimes(2); - expect(getRecordCalls(removeEmail)).toEqual([ - { domain: 'b.is-a.dev', exchanger: 'foo.bar', priority: 20 }, - { domain: 'c.is-a.dev', exchanger: 'mx1.hello.com', priority: 20 }, - ]); - - expect(addRedir).toHaveBeenCalledTimes(3); - expect(getRecordCalls(addRedir)).toEqual([ - { domain: `b.${DOMAIN_DOMAIN}`, type: 'permanent', redirect: 'https://wowow.com' }, - { domain: `d.${DOMAIN_DOMAIN}`, type: 'permanent', redirect: 'https://hhh.com' }, - { domain: `x.${DOMAIN_DOMAIN}`, type: 'permanent', redirect: 'https://example69.com' }, - ]); - expect(removeRedir).toHaveBeenCalledTimes(2); - expect(getRecordCalls(removeRedir)).toEqual([ - { domain: `b.${DOMAIN_DOMAIN}` }, - { domain: `x.${DOMAIN_DOMAIN}` }, - ]); - }); - }); }); - diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js index 077896726..896cf017d 100644 --- a/tests/domain-utils.test.js +++ b/tests/domain-utils.test.js @@ -1,9 +1,8 @@ -const { getDomains } = require('../utils/get-domain'); +const { getDomains } = require("../utils/get-domain"); -describe('getDomains', () => { - it('should resolve with the list of domains', async () => { - const list = await getDomains(); - expect(Array.isArray(list)).toBe(true); - }); +describe("getDomains", () => { + it("should resolve with the list of domains", async () => { + const list = await getDomains(); + expect(Array.isArray(list)).toBe(true); + }); }); - diff --git a/tests/domains.test.js b/tests/domains.test.js index 44c2a5422..75971b00b 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -1,31 +1,37 @@ -const R = require('ramda'); -const fs = require('fs'); -const { getDomains } = require('../utils/get-domain'); -const { validateDomainData } = require('../utils/validations'); -const { DOMAINS_PATH } = require('../utils/constants'); +const R = require("ramda"); +const fs = require("fs"); +const { getDomains } = require("../utils/get-domain"); +const { validateDomainData } = require("../utils/validations"); +const { DOMAINS_PATH } = require("../utils/constants"); -describe('Domains', () => { - it('should all be json', async () => { - const files = await fs.promises.readdir(DOMAINS_PATH, {}); - expect(files.filter(f => !/\.json$/g.test(f)).length).toBe(0); - }); +describe("Domains", () => { + it("should all be json", async () => { + const files = await fs.promises.readdir(DOMAINS_PATH, {}); + expect(files.filter((f) => !/\.json$/g.test(f)).length).toBe(0); + }); - it('should be valid', (done) => { - getDomains() - .then(R.reject(R.propEq('name', '_psl'))) - .then(R.map(data => { - const { errors } = validateDomainData(data); - if (errors.length) { - const message = errors - .map(([key, { reason }]) => `[${data.name}.${key}]: ${reason}`) - .join('\n'); - return `\nValidation errors in ${data.name}.json: \n${message}`; - } - return ''; - })) - .then(R.filter(R.complement(R.isEmpty))) - .then(messages => messages.length ? done(messages.join('\n')) : done()) - .catch(done); - }); + it("should be valid", (done) => { + getDomains() + .then(R.reject(R.propEq("name", "_psl"))) + .then( + R.map((data) => { + const { errors } = validateDomainData(data); + if (errors.length) { + const message = errors + .map( + ([key, { reason }]) => + `[${data.name}.${key}]: ${reason}`, + ) + .join("\n"); + return `\nValidation errors in ${data.name}.json: \n${message}`; + } + return ""; + }), + ) + .then(R.filter(R.complement(R.isEmpty))) + .then((messages) => + messages.length ? done(messages.join("\n")) : done(), + ) + .catch(done); + }); }); - diff --git a/tests/register.test.js b/tests/register.test.js index bd967a298..6628df211 100644 --- a/tests/register.test.js +++ b/tests/register.test.js @@ -1,209 +1,242 @@ -const R = require('ramda') -const { toHostList, registerDomains } = require('../scripts/register-domains') -const { TTL, DOMAIN_DOMAIN } = require('../utils/constants') -const { getDomainService } = require('../utils/domain-service') +const R = require("ramda"); +const { toHostList, registerDomains } = require("../scripts/register-domains"); +const { TTL, DOMAIN_DOMAIN } = require("../utils/constants"); +const { getDomainService } = require("../utils/domain-service"); const getCpanel = ({ - zone, - addZone, - removeZone, - redir, - addRedir, - removeRedir, - addEmail, - removeEmail, + zone, + addZone, + removeZone, + redir, + addRedir, + removeRedir, + addEmail, + removeEmail, } = {}) => ({ - zone: { - fetch: (_) => zone(), - add: (rec) => addZone(rec), - remove: (rec) => removeZone(rec), - }, - redirection: { - fetch: (_) => redir(), - add: (rec) => addRedir(rec), - remove: (rec) => removeRedir(rec), - }, - email: { - add: (rec) => addEmail(rec), - remove: (rec) => removeEmail(rec), - }, -}) + zone: { + fetch: (_) => zone(), + add: (rec) => addZone(rec), + remove: (rec) => removeZone(rec), + }, + redirection: { + fetch: (_) => redir(), + add: (rec) => addRedir(rec), + remove: (rec) => removeRedir(rec), + }, + email: { + add: (rec) => addEmail(rec), + remove: (rec) => removeEmail(rec), + }, +}); -describe('toHostList', () => { - it('should flatten domain data to list of hosts (without https)', () => { - const res = toHostList([ - { name: 'akshay', record: { CNAME: 'phenax.github.io' } }, - { name: 'foobar', record: { CNAME: 'v.io' } }, - { name: 'xx', record: { A: ['1.2.3.4', '5.6.3.2', '1.2.31.1'] } }, - { name: 'xx', record: { CNAME: 'foobar.com', MX: ['as.com', 'f.com'] } }, - ]) +describe("toHostList", () => { + it("should flatten domain data to list of hosts (without https)", () => { + const res = toHostList([ + { name: "akshay", record: { CNAME: "phenax.github.io" } }, + { name: "foobar", record: { CNAME: "v.io" } }, + { name: "xx", record: { A: ["1.2.3.4", "5.6.3.2", "1.2.31.1"] } }, + { + name: "xx", + record: { CNAME: "foobar.com", MX: ["as.com", "f.com"] }, + }, + ]); - expect(res).toEqual([ - { name: 'akshay', type: 'CNAME', address: 'phenax.github.io', ttl: TTL }, - { name: 'foobar', type: 'CNAME', address: 'v.io', ttl: TTL }, - { name: 'xx', type: 'A', address: '1.2.3.4', ttl: TTL }, - { name: 'xx', type: 'A', address: '5.6.3.2', ttl: TTL }, - { name: 'xx', type: 'A', address: '1.2.31.1', ttl: TTL }, - { name: 'xx', type: 'CNAME', address: 'foobar.com', ttl: TTL }, - { name: 'xx', type: 'MX', address: 'as.com', priority: 20, ttl: TTL }, - { name: 'xx', type: 'MX', address: 'f.com', priority: 21, ttl: TTL }, - ]) - }) -}) + expect(res).toEqual([ + { + name: "akshay", + type: "CNAME", + address: "phenax.github.io", + ttl: TTL, + }, + { name: "foobar", type: "CNAME", address: "v.io", ttl: TTL }, + { name: "xx", type: "A", address: "1.2.3.4", ttl: TTL }, + { name: "xx", type: "A", address: "5.6.3.2", ttl: TTL }, + { name: "xx", type: "A", address: "1.2.31.1", ttl: TTL }, + { name: "xx", type: "CNAME", address: "foobar.com", ttl: TTL }, + { + name: "xx", + type: "MX", + address: "as.com", + priority: 20, + ttl: TTL, + }, + { + name: "xx", + type: "MX", + address: "f.com", + priority: 21, + ttl: TTL, + }, + ]); + }); +}); -describe('registerDomains', () => { - const addZone = jest.fn(async () => ({})) - const removeZone = jest.fn(async () => ({})) - const addRedir = jest.fn(async () => ({})) - const removeRedir = jest.fn(async () => ({})) - const addEmail = jest.fn(async () => ({})) - const removeEmail = jest.fn(async () => ({})) +describe("registerDomains", () => { + const addZone = jest.fn(async () => ({})); + const removeZone = jest.fn(async () => ({})); + const addRedir = jest.fn(async () => ({})); + const removeRedir = jest.fn(async () => ({})); + const addEmail = jest.fn(async () => ({})); + const removeEmail = jest.fn(async () => ({})); - const mockDS = ({ zones, redirections }) => - getDomainService({ - cpanel: getCpanel({ - zone: async () => zones, - redir: async () => redirections, - addZone, - addEmail, - addRedir, - removeZone, - removeRedir, - removeEmail, - }), - }) + const mockDS = ({ zones, redirections }) => + getDomainService({ + cpanel: getCpanel({ + zone: async () => zones, + redir: async () => redirections, + addZone, + addEmail, + addRedir, + removeZone, + removeRedir, + removeEmail, + }), + }); - beforeEach(() => { - addZone.mockClear() - removeZone.mockClear() - addRedir.mockClear() - removeRedir.mockClear() - addEmail.mockClear() - removeEmail.mockClear() - }) + beforeEach(() => { + addZone.mockClear(); + removeZone.mockClear(); + addRedir.mockClear(); + removeRedir.mockClear(); + addEmail.mockClear(); + removeEmail.mockClear(); + }); - it('should register the new set of hosts generated from domains list', async () => { - const localHosts = [ - { name: 'a', record: { CNAME: 'hello' } }, - { name: 'b', record: { CNAME: 'xaa' } }, - ] - const remoteHosts = [ - { line: 1, name: 'a', type: 'CNAME', address: 'hello' }, - { line: 2, name: 'b', type: 'CNAME', address: 'goo' }, - { line: 3, name: 'b', type: 'CNAME', address: 'xaa' }, - ] - const remoteRedirections = [] + it("should register the new set of hosts generated from domains list", async () => { + const localHosts = [ + { name: "a", record: { CNAME: "hello" } }, + { name: "b", record: { CNAME: "xaa" } }, + ]; + const remoteHosts = [ + { line: 1, name: "a", type: "CNAME", address: "hello" }, + { line: 2, name: "b", type: "CNAME", address: "goo" }, + { line: 3, name: "b", type: "CNAME", address: "xaa" }, + ]; + const remoteRedirections = []; - const domainService = mockDS({ - zones: remoteHosts, - redirections: remoteRedirections, - }) - await registerDomains({ getDomains: async () => localHosts, domainService }) + const domainService = mockDS({ + zones: remoteHosts, + redirections: remoteRedirections, + }); + await registerDomains({ + getDomains: async () => localHosts, + domainService, + }); - expect(addZone).toHaveBeenCalledTimes(0) - expect(removeZone).toHaveBeenCalledTimes(1) - expect(addRedir).toHaveBeenCalledTimes(0) - expect(removeRedir).toHaveBeenCalledTimes(0) - }) + expect(addZone).toHaveBeenCalledTimes(0); + expect(removeZone).toHaveBeenCalledTimes(1); + expect(addRedir).toHaveBeenCalledTimes(0); + expect(removeRedir).toHaveBeenCalledTimes(0); + }); - it('should add the new set hosts', async () => { - const localHosts = [ - { name: 'a', record: { CNAME: 'boo' } }, - { - name: 'b', - record: { A: ['1.1.1.1', '1.1.1.2'], MX: 'somemx', TXT: 'some txt' }, - }, - { name: 'c', record: { URL: 'https://google.com' } }, - { name: 'd', record: { CNAME: 'foobar' } }, - { name: 'e', record: { A: ['2.2.2.2'], TXT: ['some', 'extra', 'txt'] } }, - ] - const remoteHosts = [ - { line: 1, name: 'a', type: 'CNAME', address: 'boo' }, - { line: 2, name: 'b', type: 'MX', address: 'othermx' }, - { line: 3, name: 'd', type: 'CNAME', address: 'foobaz' }, - ] - const remoteRedirections = [ - { domain: `b.${DOMAIN_DOMAIN}`, destination: 'x' }, - { domain: `a.${DOMAIN_DOMAIN}`, destination: 'y' }, - ] + it("should add the new set hosts", async () => { + const localHosts = [ + { name: "a", record: { CNAME: "boo" } }, + { + name: "b", + record: { + A: ["1.1.1.1", "1.1.1.2"], + MX: "somemx", + TXT: "some txt", + }, + }, + { name: "c", record: { URL: "https://google.com" } }, + { name: "d", record: { CNAME: "foobar" } }, + { + name: "e", + record: { A: ["2.2.2.2"], TXT: ["some", "extra", "txt"] }, + }, + ]; + const remoteHosts = [ + { line: 1, name: "a", type: "CNAME", address: "boo" }, + { line: 2, name: "b", type: "MX", address: "othermx" }, + { line: 3, name: "d", type: "CNAME", address: "foobaz" }, + ]; + const remoteRedirections = [ + { domain: `b.${DOMAIN_DOMAIN}`, destination: "x" }, + { domain: `a.${DOMAIN_DOMAIN}`, destination: "y" }, + ]; - const domainService = mockDS({ - zones: remoteHosts, - redirections: remoteRedirections, - }) - await registerDomains({ getDomains: async () => localHosts, domainService }) + const domainService = mockDS({ + zones: remoteHosts, + redirections: remoteRedirections, + }); + await registerDomains({ + getDomains: async () => localHosts, + domainService, + }); - expect(addZone).toHaveBeenCalledTimes(8) - expect(addZone.mock.calls).toEqual([ - [{ name: 'b', type: 'A', address: '1.1.1.1', line: undefined }], - [{ name: 'b', type: 'A', address: '1.1.1.2', line: undefined }], - [ - { - name: 'b', - type: 'TXT', - address: 'some txt', - txtdata: 'some txt', - line: undefined, - }, - ], - [ - { - name: 'd', - type: 'CNAME', - cname: 'foobar', - address: 'foobar', - line: undefined, - }, - ], - [{ name: 'e', type: 'A', address: '2.2.2.2', line: undefined }], - [ - { - name: 'e', - type: 'TXT', - address: 'some', - txtdata: 'some', - line: undefined, - }, - ], - [ - { - name: 'e', - type: 'TXT', - address: 'extra', - txtdata: 'extra', - line: undefined, - }, - ], - [ - { - name: 'e', - type: 'TXT', - address: 'txt', - txtdata: 'txt', - line: undefined, - }, - ], - ]) + expect(addZone).toHaveBeenCalledTimes(8); + expect(addZone.mock.calls).toEqual([ + [{ name: "b", type: "A", address: "1.1.1.1", line: undefined }], + [{ name: "b", type: "A", address: "1.1.1.2", line: undefined }], + [ + { + name: "b", + type: "TXT", + address: "some txt", + txtdata: "some txt", + line: undefined, + }, + ], + [ + { + name: "d", + type: "CNAME", + cname: "foobar", + address: "foobar", + line: undefined, + }, + ], + [{ name: "e", type: "A", address: "2.2.2.2", line: undefined }], + [ + { + name: "e", + type: "TXT", + address: "some", + txtdata: "some", + line: undefined, + }, + ], + [ + { + name: "e", + type: "TXT", + address: "extra", + txtdata: "extra", + line: undefined, + }, + ], + [ + { + name: "e", + type: "TXT", + address: "txt", + txtdata: "txt", + line: undefined, + }, + ], + ]); - expect(removeZone).toHaveBeenCalledTimes(1) - expect(removeZone.mock.calls).toEqual([[{ line: 3 }]]) + expect(removeZone).toHaveBeenCalledTimes(1); + expect(removeZone.mock.calls).toEqual([[{ line: 3 }]]); - expect(addRedir).toHaveBeenCalledTimes(1) - expect(addRedir.mock.calls).toEqual([ - [ - { - domain: 'c.booboo.xyz', - redirect: 'https://google.com', - redirect_wildcard: 1, - redirect_www: 1, - type: 'permanent', - }, - ], - ]) + expect(addRedir).toHaveBeenCalledTimes(1); + expect(addRedir.mock.calls).toEqual([ + [ + { + domain: "c.booboo.xyz", + redirect: "https://google.com", + redirect_wildcard: 1, + redirect_www: 1, + type: "permanent", + }, + ], + ]); - expect(addEmail).toHaveBeenCalledTimes(1) - expect(addEmail.mock.calls).toEqual([ - [{ domain: 'b.is-a.dev', exchanger: 'somemx', priority: 20 }], - ]) - }) -}) + expect(addEmail).toHaveBeenCalledTimes(1); + expect(addEmail.mock.calls).toEqual([ + [{ domain: "b.is-a.dev", exchanger: "somemx", priority: 20 }], + ]); + }); +}); diff --git a/tests/validations.test.js b/tests/validations.test.js index bb2cac176..6f4c85daf 100644 --- a/tests/validations.test.js +++ b/tests/validations.test.js @@ -1,129 +1,163 @@ -const { validateDomainData, isValidDomain } = require('../utils/validations'); -const INVALID_NAMES = require('../utils/invalid-domains.json'); +const { validateDomainData, isValidDomain } = require("../utils/validations"); +const INVALID_NAMES = require("../utils/invalid-domains.json"); const defaultDomain = { - name: 'aaa', - record: { - A: ['121.121.121.121'] - }, - owner: { - username: 'betsy', - email: 'betsyfuckyoassup@foobar.com', - }, + name: "aaa", + record: { + A: ["121.121.121.121"], + }, + owner: { + username: "betsy", + email: "betsyfuckyoassup@foobar.com", + }, }; -const getstroflen = len => Array(len).fill('a').join(''); +const getstroflen = (len) => Array(len).fill("a").join(""); -describe('isValidMX', () => { - it('should be valid mx record', () => { - const cases = [ - { mx: 'foobar.com', result: true }, - { mx: 'as.as', result: true }, - { mx: 'ASPMX.L.GOOGLE.COM', result: true }, - { mx: 'ALT4.ASPMX.L.GOOGLE.COM', result: true }, - { mx: 'hello', result: false }, - { mx: 'helalsds-asd5sjdsd.com', result: true }, - { mx: 'helalsds?asd5sjdsd.com', result: false }, - { mx: 'helalsds_asd5sjdsd.com', result: false }, +describe("isValidMX", () => { + it("should be valid mx record", () => { + const cases = [ + { mx: "foobar.com", result: true }, + { mx: "as.as", result: true }, + { mx: "ASPMX.L.GOOGLE.COM", result: true }, + { mx: "ALT4.ASPMX.L.GOOGLE.COM", result: true }, + { mx: "hello", result: false }, + { mx: "helalsds-asd5sjdsd.com", result: true }, + { mx: "helalsds?asd5sjdsd.com", result: false }, + { mx: "helalsds_asd5sjdsd.com", result: false }, + ]; + + cases.forEach(({ mx, result }) => { + expect(isValidDomain(mx)).toBe(result); + }); + }); +}); + +describe("validateDomainData", () => { + const invalidCases = [ + {}, + { name: "helo" }, + { name: "wwow", record: { A: ["12312"] } }, + ...[ + "", + " ", + undefined, + "hlo wld", + "g32++13", + "ajsdD_123yq", + "khsda%", + "122*dsd", + getstroflen(101), + ].map((name) => ({ + ...defaultDomain, + name, + })), + { ...defaultDomain, record: { CNAME: "sd", A: ["121,3213"] } }, + { ...defaultDomain, record: { A: ["121", "12"], FOOBAR: ["sd"] } }, + { ...defaultDomain, record: { A: [] } }, + { ...defaultDomain, owner: {} }, + { ...defaultDomain, owner: { username: "hwelo" } }, + { ...defaultDomain, owner: { email: "hwelo" } }, + { ...defaultDomain, record: { CNAME: "http://foobar.com" } }, + { ...defaultDomain, record: { CNAME: "https://foobar.com" } }, + { ...defaultDomain, record: { URL: "foobar.com" } }, + { + ...defaultDomain, + record: { CNAME: "foobar.com", A: ["11.22.22.33"] }, + }, + { + ...defaultDomain, + record: { CNAME: "foobar.com", MX: ["ALT4.ASPMX.L.GOOGLE.COM"] }, + }, + ...INVALID_NAMES.map((name) => ({ ...defaultDomain, name })).slice( + 0, + 1, + ), + { ...defaultDomain, name: "a.b" }, + { ...defaultDomain, name: "ww2.baa" }, + { ...defaultDomain, name: "help.baa" }, + { ...defaultDomain, name: "_github-pages-challenge-is-a-dev" }, + { ...defaultDomain, name: "_github-challenge-is-a-dev" }, + { ...defaultDomain, record: { AAAA: [] } }, + { ...defaultDomain, record: { AAAA: ["182.22.222.22", "::1"] } }, + { ...defaultDomain, record: { AAAA: "182.22.222.22" } }, + { ...defaultDomain, record: { A: "::1" } }, + { ...defaultDomain, name: "_discord" }, + { ...defaultDomain, name: "_gitlab-pages-verification-code" }, + { ...defaultDomain, name: "_acme-challenge" }, + { ...defaultDomain, name: "_dmarc" }, + { ...defaultDomain, name: "_gh-is-a-dev" }, + { ...defaultDomain, name: "_domainkey" }, ]; - cases.forEach(({ mx, result }) => { - expect(isValidDomain(mx)).toBe(result); + const validCases = [ + defaultDomain, + ...[ + "hello", + "hello-world", + "11111111111", + "--wow--", + "wow--", + "--wow", + ].map((name) => ({ + ...defaultDomain, + name, + })), + { + ...defaultDomain, + description: getstroflen(99), + }, + { ...defaultDomain, record: { CNAME: "aa.sd" } }, + { ...defaultDomain, record: { URL: "https://foobar.com" } }, + { ...defaultDomain, record: { URL: "http://foobar.com/foobar/" } }, + { ...defaultDomain, record: { MX: ["ALT4.ASPMX.L.GOOGLE.COM"] } }, + { ...defaultDomain, record: { TXT: "foobar wow nice!!!" } }, + { + ...defaultDomain, + record: { A: ["1.1.1.1"], MX: ["mx1.example.com"] }, + }, + { ...defaultDomain, name: "gogo.foo.bar" }, + { ...defaultDomain, name: "ww9.baa" }, + { ...defaultDomain, name: "_github-pages-challenge-phenax.akshay" }, + { ...defaultDomain, name: "_github-pages-challenge-hello01-ga" }, + { ...defaultDomain, name: "_github-pages-challenge-hello01_ga" }, + { ...defaultDomain, name: "_github-challenge-phenax.akshay" }, + { ...defaultDomain, name: "_github-challenge-hello01-ga" }, + { ...defaultDomain, name: "_github-challenge-hello01_ga" }, + { + ...defaultDomain, + record: { TXT: ["foobar wow nice!!!", "more text"] }, + }, + { + ...defaultDomain, + record: { AAAA: ["::1", "2001:db8:3333:4444:5555:6666:7777:8888"] }, + }, + { ...defaultDomain, record: { A: ["122.222.222.222"] } }, + { ...defaultDomain, name: "_discord.subdomain" }, + { ...defaultDomain, name: "_gitlab-pages-verification-code.subdomain" }, + { ...defaultDomain, name: "_acme-challenge.subdomain" }, + { ...defaultDomain, name: "_dmarc.subdomain" }, + { ...defaultDomain, name: "_gh-phenax.akshay" }, + { ...defaultDomain, name: "_gh-hello01-ga" }, + { ...defaultDomain, name: "_gh-hello01_ga" }, + { ...defaultDomain, name: "_domainkey.subdomain" }, + { ...defaultDomain, name: "mx._domainkey.subdomain" }, + ]; + + it("should return false for invalid data", () => { + invalidCases.forEach((data) => { + const { valid, errors } = validateDomainData(data); + expect(valid).toBe(false); + expect(errors.length).toBeGreaterThan(0); + }); + }); + + it("should return true if the name is valid", () => { + validCases.forEach((data) => { + const { valid, errors } = validateDomainData(data); + if (!valid) console.log(JSON.stringify(errors, null, 2)); + expect(valid).toBe(true); + expect(errors).toEqual([]); + }); }); - }); }); - -describe('validateDomainData', () => { - const invalidCases = [ - {}, - { name: 'helo' }, - { name: 'wwow', record: { A: ['12312'] } }, - ...['', ' ', undefined, 'hlo wld', 'g32++13', 'ajsdD_123yq', 'khsda%', '122*dsd', getstroflen(101)].map(name => ({ - ...defaultDomain, - name, - })), - { ...defaultDomain, record: { CNAME: 'sd', A: ['121,3213'] } }, - { ...defaultDomain, record: { A: ['121', '12'], FOOBAR: ['sd'] } }, - { ...defaultDomain, record: { A: [] } }, - { ...defaultDomain, owner: {}, }, - { ...defaultDomain, owner: { username: 'hwelo', }, }, - { ...defaultDomain, owner: { email: 'hwelo' }, }, - { ...defaultDomain, record: { CNAME: 'http://foobar.com' } }, - { ...defaultDomain, record: { CNAME: 'https://foobar.com' } }, - { ...defaultDomain, record: { URL: 'foobar.com' } }, - { ...defaultDomain, record: { CNAME: 'foobar.com', A: ['11.22.22.33'] } }, - { ...defaultDomain, record: { CNAME: 'foobar.com', MX: ['ALT4.ASPMX.L.GOOGLE.COM'] } }, - ...INVALID_NAMES.map(name => ({ ...defaultDomain, name })).slice(0, 1), - { ...defaultDomain, name: 'a.b' }, - { ...defaultDomain, name: 'ww2.baa' }, - { ...defaultDomain, name: 'help.baa' }, - { ...defaultDomain, name: '_github-pages-challenge-is-a-dev' }, - { ...defaultDomain, name: '_github-challenge-is-a-dev' }, - { ...defaultDomain, record: { AAAA: [] } }, - { ...defaultDomain, record: { AAAA: ['182.22.222.22', '::1'] } }, - { ...defaultDomain, record: { AAAA: '182.22.222.22' } }, - { ...defaultDomain, record: { A: '::1' } }, - { ...defaultDomain, name: '_discord' }, - { ...defaultDomain, name: '_gitlab-pages-verification-code' }, - { ...defaultDomain, name: '_acme-challenge' }, - { ...defaultDomain, name: '_dmarc' }, - { ...defaultDomain, name: '_gh-is-a-dev' }, - { ...defaultDomain, name: '_domainkey' }, - ]; - - const validCases = [ - defaultDomain, - ...['hello', 'hello-world', '11111111111', '--wow--', 'wow--', '--wow'].map(name => ({ - ...defaultDomain, - name, - })), - { - ...defaultDomain, - description: getstroflen(99), - }, - { ...defaultDomain, record: { CNAME: 'aa.sd' } }, - { ...defaultDomain, record: { URL: 'https://foobar.com' } }, - { ...defaultDomain, record: { URL: 'http://foobar.com/foobar/' } }, - { ...defaultDomain, record: { MX: ['ALT4.ASPMX.L.GOOGLE.COM'] } }, - { ...defaultDomain, record: { TXT: 'foobar wow nice!!!' } }, - { ...defaultDomain, record: { A: ['1.1.1.1'], MX: ['mx1.example.com'] } }, - { ...defaultDomain, name: 'gogo.foo.bar' }, - { ...defaultDomain, name: 'ww9.baa' }, - { ...defaultDomain, name: '_github-pages-challenge-phenax.akshay' }, - { ...defaultDomain, name: '_github-pages-challenge-hello01-ga' }, - { ...defaultDomain, name: '_github-pages-challenge-hello01_ga' }, - { ...defaultDomain, name: '_github-challenge-phenax.akshay' }, - { ...defaultDomain, name: '_github-challenge-hello01-ga' }, - { ...defaultDomain, name: '_github-challenge-hello01_ga' }, - { ...defaultDomain, record: { TXT: ['foobar wow nice!!!', 'more text'] } }, - { ...defaultDomain, record: { AAAA: ['::1', '2001:db8:3333:4444:5555:6666:7777:8888'] } }, - { ...defaultDomain, record: { A: ['122.222.222.222'] } }, - { ...defaultDomain, name: '_discord.subdomain' }, - { ...defaultDomain, name: '_gitlab-pages-verification-code.subdomain' }, - { ...defaultDomain, name: '_acme-challenge.subdomain' }, - { ...defaultDomain, name: '_dmarc.subdomain' }, - { ...defaultDomain, name: '_gh-phenax.akshay' }, - { ...defaultDomain, name: '_gh-hello01-ga' }, - { ...defaultDomain, name: '_gh-hello01_ga' }, - { ...defaultDomain, name: '_domainkey.subdomain' }, - { ...defaultDomain, name: 'mx._domainkey.subdomain' }, - ]; - - it('should return false for invalid data', () => { - invalidCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - expect(valid).toBe(false); - expect(errors.length).toBeGreaterThan(0); - }); - }); - - it('should return true if the name is valid', () => { - validCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - if (!valid) console.log(JSON.stringify(errors, null, 2)); - expect(valid).toBe(true); - expect(errors).toEqual([]); - }); - }); -}); - diff --git a/utils/constants.js b/utils/constants.js index 19430e5bf..b0cf0079d 100644 --- a/utils/constants.js +++ b/utils/constants.js @@ -1,30 +1,30 @@ -const path = require('path'); +const path = require("path"); -const { NODE_ENV: ENV = 'test' } = process.env; +const { NODE_ENV: ENV = "test" } = process.env; const { - DOMAIN_USER, - DOMAIN_API_KEY, - DOMAIN_DOMAIN, - DOMAIN_API_HOST, - DOMAIN_API_PORT, - DOMAIN_HOST_IP, + DOMAIN_USER, + DOMAIN_API_KEY, + DOMAIN_DOMAIN, + DOMAIN_API_HOST, + DOMAIN_API_PORT, + DOMAIN_HOST_IP, } = process.env; -const IS_TEST = ENV === 'test'; +const IS_TEST = ENV === "test"; -const DOMAINS_PATH = path.resolve('domains'); +const DOMAINS_PATH = path.resolve("domains"); module.exports = { - ENV, - IS_TEST, - VALID_RECORD_TYPES: ['CNAME', 'A', 'URL', 'MX', 'TXT', 'AAAA'], - DOMAIN_DOMAIN: DOMAIN_DOMAIN || 'booboo.xyz', - DOMAIN_USER: IS_TEST ? 'testuser' : DOMAIN_USER, - DOMAIN_API_KEY: IS_TEST ? 'testkey' : DOMAIN_API_KEY, - DOMAIN_API_HOST: IS_TEST ? 'example.com' : DOMAIN_API_HOST, - DOMAIN_API_PORT: IS_TEST ? 6969 : DOMAIN_API_PORT, - DOMAIN_HOST_IP, - DOMAINS_PATH, - TTL: 5 * 60 * 60, + ENV, + IS_TEST, + VALID_RECORD_TYPES: ["CNAME", "A", "URL", "MX", "TXT", "AAAA"], + DOMAIN_DOMAIN: DOMAIN_DOMAIN || "booboo.xyz", + DOMAIN_USER: IS_TEST ? "testuser" : DOMAIN_USER, + DOMAIN_API_KEY: IS_TEST ? "testkey" : DOMAIN_API_KEY, + DOMAIN_API_HOST: IS_TEST ? "example.com" : DOMAIN_API_HOST, + DOMAIN_API_PORT: IS_TEST ? 6969 : DOMAIN_API_PORT, + DOMAIN_HOST_IP, + DOMAINS_PATH, + TTL: 5 * 60 * 60, }; diff --git a/utils/domain-service.js b/utils/domain-service.js index fe75aef04..b76eeac01 100644 --- a/utils/domain-service.js +++ b/utils/domain-service.js @@ -1,167 +1,206 @@ -const R = require('ramda'); -const { cpanel } = require('./lib/cpanel'); -const { DOMAIN_DOMAIN, VALID_RECORD_TYPES } = require('./constants'); -const { then, log, print, lazyTask, batchLazyTasks } = require('./helpers'); +const R = require("ramda"); +const { cpanel } = require("./lib/cpanel"); +const { DOMAIN_DOMAIN, VALID_RECORD_TYPES } = require("./constants"); +const { then, log, print, lazyTask, batchLazyTasks } = require("./helpers"); const BATCH_SIZE = 1; const recordToRedirection = ({ name, address }) => ({ - domain: name === '@' ? DOMAIN_DOMAIN : `${name}.${DOMAIN_DOMAIN}`, - redirect: address, - type: 'permanent', - redirect_wildcard: 1, - redirect_www: 1, + domain: name === "@" ? DOMAIN_DOMAIN : `${name}.${DOMAIN_DOMAIN}`, + redirect: address, + type: "permanent", + redirect_wildcard: 1, + redirect_www: 1, }); const recordToZone = ({ name, type, address, id, priority }) => ({ - line: id, - name: name === '@' ? `${DOMAIN_DOMAIN}.` : name, - type, - address, - ...(type === 'MX' ? { priority } : {}), - ...(type === 'CNAME' ? { cname: address } : {}), - ...(type === 'TXT' ? { txtdata: address } : {}), + line: id, + name: name === "@" ? `${DOMAIN_DOMAIN}.` : name, + type, + address, + ...(type === "MX" ? { priority } : {}), + ...(type === "CNAME" ? { cname: address } : {}), + ...(type === "TXT" ? { txtdata: address } : {}), }); -const cleanName = name => - [DOMAIN_DOMAIN, `${DOMAIN_DOMAIN}.`].includes(name) ? '@' : `${name}`.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), '').toLowerCase(); +const cleanName = (name) => + [DOMAIN_DOMAIN, `${DOMAIN_DOMAIN}.`].includes(name) + ? "@" + : `${name}` + .replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), "") + .toLowerCase(); const zoneToRecord = ({ - name, - type, - cname, - address, - priority, - preference, - exchange, - record, - line: id -}) => - ({ + name, + type, + cname, + address, + priority, + preference, + exchange, + record, + line: id, +}) => ({ id, name: cleanName(name), type: `${type}`, - address: `${exchange || cname || address || record}`.replace(/\.$/g, '').toLowerCase(), + address: `${exchange || cname || address || record}` + .replace(/\.$/g, "") + .toLowerCase(), priority: priority || preference, - }); +}); const redirectionToRecord = ({ domain, destination }) => ({ - id: domain, - name: cleanName(domain), - type: 'URL', - address: `${destination}`.replace(/\/$/g, ''), + id: domain, + name: cleanName(domain), + type: "URL", + address: `${destination}`.replace(/\/$/g, ""), }); const recordToEmailMx = ({ name, address, priority }) => ({ - domain: `${name}.is-a.dev`, - exchanger: address, - priority, -}) + domain: `${name}.is-a.dev`, + exchanger: address, + priority, +}); -const getHostKey = host => - `${host.name.toLowerCase()}##${host.type.toLowerCase()}##${host.address.toLowerCase()}`; +const getHostKey = (host) => + `${host.name.toLowerCase()}##${host.type.toLowerCase()}##${host.address.toLowerCase()}`; const isReserved = (domain) => - domain.name.startsWith('*') || !VALID_RECORD_TYPES.includes(domain.type) + domain.name.startsWith("*") || !VALID_RECORD_TYPES.includes(domain.type); const diffRecords = (oldRecords, newRecords) => { - const isMatchingRecord = (a, b) => getHostKey(a) === getHostKey(b); + const isMatchingRecord = (a, b) => getHostKey(a) === getHostKey(b); - const remove = R.differenceWith(isMatchingRecord, oldRecords, newRecords); - const add = R.differenceWith(isMatchingRecord, newRecords, oldRecords); + const remove = R.differenceWith(isMatchingRecord, oldRecords, newRecords); + const add = R.differenceWith(isMatchingRecord, newRecords, oldRecords); - return { add, remove }; + return { add, remove }; }; -const executeBatch = (batches) => batches.reduce((promise, batch, index) => { - return promise.then(async () => { - log('>>> Running batch number:', index + 1, `(size: ${batch.length})`); +const executeBatch = (batches) => + batches.reduce((promise, batch, index) => { + return promise.then(async () => { + log( + ">>> Running batch number:", + index + 1, + `(size: ${batch.length})`, + ); - const values = await Promise.all(batch.map(fn => fn().catch(e => console.error(e)))); + const values = await Promise.all( + batch.map((fn) => fn().catch((e) => console.error(e))), + ); - const results = values.map(data => R.pathOr({ result: data }, ['cpanelresult', 'data', 0], data)); - const failed = results.filter(x => (x.result || {}).status != 1); + const results = values.map((data) => + R.pathOr({ result: data }, ["cpanelresult", "data", 0], data), + ); + const failed = results.filter((x) => (x.result || {}).status != 1); - log(`${values.length - failed.length}/${values.length}`); - failed.length && log(JSON.stringify(failed, null, 2)); + log(`${values.length - failed.length}/${values.length}`); + failed.length && log(JSON.stringify(failed, null, 2)); - return null; - }); -}, Promise.resolve()); + return null; + }); + }, Promise.resolve()); const getDomainService = ({ cpanel }) => { - const fetchZoneRecords = R.compose( - then(R.reject(isReserved)), - then(R.map(zoneToRecord)), - cpanel.zone.fetch - ); - const fetchRedirections = R.compose( - then(R.map(redirectionToRecord)), - cpanel.redirection.fetch - ); + const fetchZoneRecords = R.compose( + then(R.reject(isReserved)), + then(R.map(zoneToRecord)), + cpanel.zone.fetch, + ); + const fetchRedirections = R.compose( + then(R.map(redirectionToRecord)), + cpanel.redirection.fetch, + ); - const addZoneRecord = lazyTask(R.compose( - R.ifElse(R.propEq('type', 'MX'), - R.compose(cpanel.email.add, recordToEmailMx), - cpanel.zone.add - ), - recordToZone, - print(r => `Adding zone for ${r.name}: (${r.type} ${r.address})...`), - )); - const removeZoneRecord = lazyTask(R.compose( - R.ifElse(R.propEq('type', 'MX'), - R.compose(cpanel.email.remove, recordToEmailMx), - R.compose(cpanel.zone.remove, R.pick(['line'])) - ), - recordToZone, - print(r => `Deleting zone for ${r.name}: (${r.type} ${r.address})...`), - )); - const addRedirection = lazyTask(R.compose( - cpanel.redirection.add, - recordToRedirection, - print(({ name }) => `Adding redirection for ${name}`), - )); - const removeRedirection = lazyTask(R.compose( - cpanel.redirection.remove, - R.pick(['domain']), - recordToRedirection, - print(({ name }) => `Deleting redirection for ${name}`), - )); + const addZoneRecord = lazyTask( + R.compose( + R.ifElse( + R.propEq("type", "MX"), + R.compose(cpanel.email.add, recordToEmailMx), + cpanel.zone.add, + ), + recordToZone, + print( + (r) => `Adding zone for ${r.name}: (${r.type} ${r.address})...`, + ), + ), + ); + const removeZoneRecord = lazyTask( + R.compose( + R.ifElse( + R.propEq("type", "MX"), + R.compose(cpanel.email.remove, recordToEmailMx), + R.compose(cpanel.zone.remove, R.pick(["line"])), + ), + recordToZone, + print( + (r) => + `Deleting zone for ${r.name}: (${r.type} ${r.address})...`, + ), + ), + ); + const addRedirection = lazyTask( + R.compose( + cpanel.redirection.add, + recordToRedirection, + print(({ name }) => `Adding redirection for ${name}`), + ), + ); + const removeRedirection = lazyTask( + R.compose( + cpanel.redirection.remove, + R.pick(["domain"]), + recordToRedirection, + print(({ name }) => `Deleting redirection for ${name}`), + ), + ); - const getHosts = () => - Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten); + const getHosts = () => + Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten); - const addRecords = R.compose( - batchLazyTasks(BATCH_SIZE), - R.filter(Boolean), - R.map(R.cond([ - [R.propEq('type', 'URL'), addRedirection], - [R.T, addZoneRecord], - ])), - ); - const removeRecords = R.compose( - batchLazyTasks(BATCH_SIZE), - R.map(R.cond([ [ R.propEq('type', 'URL'), removeRedirection ], [ R.T, removeZoneRecord ] ])), - R.sort((a, b) => b.id - a.id) - ); + const addRecords = R.compose( + batchLazyTasks(BATCH_SIZE), + R.filter(Boolean), + R.map( + R.cond([ + [R.propEq("type", "URL"), addRedirection], + [R.T, addZoneRecord], + ]), + ), + ); + const removeRecords = R.compose( + batchLazyTasks(BATCH_SIZE), + R.map( + R.cond([ + [R.propEq("type", "URL"), removeRedirection], + [R.T, removeZoneRecord], + ]), + ), + R.sort((a, b) => b.id - a.id), + ); - const updateHosts = async hosts => { - const remoteHostList = await getHosts(); - const { add, remove } = diffRecords(remoteHostList, hosts); - console.log(`Adding ${add.length}; Removing ${remove.length}`) + const updateHosts = async (hosts) => { + const remoteHostList = await getHosts(); + const { add, remove } = diffRecords(remoteHostList, hosts); + console.log(`Adding ${add.length}; Removing ${remove.length}`); - await executeBatch([ - ...removeRecords(remove), - ...addRecords(add), - ]); - return { added: add.length, removed: remove.length }; - }; + await executeBatch([...removeRecords(remove), ...addRecords(add)]); + return { added: add.length, removed: remove.length }; + }; - return { getHosts, get: cpanel.zone.fetch, addZoneRecord, removeZoneRecord, updateHosts }; + return { + getHosts, + get: cpanel.zone.fetch, + addZoneRecord, + removeZoneRecord, + updateHosts, + }; }; const domainService = getDomainService({ cpanel }); module.exports = { - getDomainService, - domainService, - diffRecords, + getDomainService, + domainService, + diffRecords, }; diff --git a/utils/get-domain.js b/utils/get-domain.js index d6529032d..064240358 100644 --- a/utils/get-domain.js +++ b/utils/get-domain.js @@ -1,22 +1,27 @@ -const fs = require('fs'); -const path = require('path'); -const R = require('ramda'); -const {DOMAINS_PATH} = require('./constants'); +const fs = require("fs"); +const path = require("path"); +const R = require("ramda"); +const { DOMAINS_PATH } = require("./constants"); -const toDomain = str => path.join(DOMAINS_PATH, str); +const toDomain = (str) => path.join(DOMAINS_PATH, str); -const parseDomain = name => str => { - try {return JSON.parse(str);} - catch (e) {throw new Error(`Error: Cant parse ${name} => ${str}`);} +const parseDomain = (name) => (str) => { + try { + return JSON.parse(str); + } catch (e) { + throw new Error(`Error: Could not parse ${name} => ${str}`); + } }; -const toDomainData = name => R.compose(parseDomain(name), R.toString, fs.readFileSync, toDomain)(name); +const toDomainData = (name) => + R.compose(parseDomain(name), R.toString, fs.readFileSync, toDomain)(name); const getDomains = () => - fs.promises.readdir(DOMAINS_PATH, {}) - .then(R.map(name => ({ - ...toDomainData(name), - name: name.replace(/\.json$/, ''), - }))); + fs.promises.readdir(DOMAINS_PATH, {}).then( + R.map((name) => ({ + ...toDomainData(name), + name: name.replace(/\.json$/, ""), + })), + ); -module.exports = {getDomains}; +module.exports = { getDomains }; diff --git a/utils/helpers.js b/utils/helpers.js index 171dce8cc..03369f2b7 100644 --- a/utils/helpers.js +++ b/utils/helpers.js @@ -1,52 +1,54 @@ -const R = require('ramda'); -const { IS_TEST } = require('./constants'); +const R = require("ramda"); +const { IS_TEST } = require("./constants"); const log = IS_TEST ? () => {} : console.log; -const print = fn => x => log(fn(x)) || x; +const print = (fn) => (x) => log(fn(x)) || x; -const between = (min, max) => num => num >= min && num <= max; -const testRegex = regex => str => !!(str && str.match(regex)); +const between = (min, max) => (num) => num >= min && num <= max; +const testRegex = (regex) => (str) => !!(str && str.match(regex)); -const validate = pattern => data => R.compose( - invalidPairs => invalidPairs.length - ? { errors: invalidPairs, valid: false } - : { errors: [], valid: true }, - R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), - R.toPairs, -)(pattern); +const validate = (pattern) => (data) => + R.compose( + (invalidPairs) => + invalidPairs.length + ? { errors: invalidPairs, valid: false } + : { errors: [], valid: true }, + R.filter(([key, { fn }]) => (fn ? !fn(data[key]) : false)), + R.toPairs, + )(pattern); const or = R.anyPass; const and = R.allPass; -const then = fn => p => p.then(fn); +const then = (fn) => (p) => p.then(fn); -const lazyTask = fn => data => () => fn(data); +const lazyTask = (fn) => (data) => () => fn(data); -const batchLazyTasks = count => tasks => tasks.reduce((batches, task) => { - if (batches.length === 0) return [[task]]; +const batchLazyTasks = (count) => (tasks) => + tasks.reduce((batches, task) => { + if (batches.length === 0) return [[task]]; - const full = R.init(batches); - const last = R.last(batches); + const full = R.init(batches); + const last = R.last(batches); - if (last.length >= count) return [...batches, [task]]; - return [...full, [...last, task]]; -}, []); + if (last.length >= count) return [...batches, [task]]; + return [...full, [...last, task]]; + }, []); -const withLengthGte = n => R.compose(R.gte(R.__, n), R.length); -const withLengthEq = n => R.compose(R.equals(n), R.length); +const withLengthGte = (n) => R.compose(R.gte(R.__, n), R.length); +const withLengthEq = (n) => R.compose(R.equals(n), R.length); module.exports = { - or, - and, - validate, - between, - testRegex, - log, - print, - then, - lazyTask, - batchLazyTasks, - withLengthEq, - withLengthGte, + or, + and, + validate, + between, + testRegex, + log, + print, + then, + lazyTask, + batchLazyTasks, + withLengthEq, + withLengthGte, }; - diff --git a/utils/invalid-domains.json b/utils/invalid-domains.json index 7c3a1ea1d..c3ab5e2f8 100644 --- a/utils/invalid-domains.json +++ b/utils/invalid-domains.json @@ -1,24 +1,24 @@ [ - "_acme-challenge", - "_discord", - "_dmarc", - "_domainkey", - "_gh-is-a-dev", - "_github-challenge-is-a-dev", - "_github-pages-challenge-is-a-dev", - "_gitlab-pages-verification-code", - "con", - "help", - "no-reply", - "noreply", - "notification", - "notifications", - "support", - "ww", - "ww1", - "ww2", - "ww3", - "ww4", - "wwww", - "your-domain-name" + "_acme-challenge", + "_discord", + "_dmarc", + "_domainkey", + "_gh-is-a-dev", + "_github-challenge-is-a-dev", + "_github-pages-challenge-is-a-dev", + "_gitlab-pages-verification-code", + "con", + "help", + "no-reply", + "noreply", + "notification", + "notifications", + "support", + "ww", + "ww1", + "ww2", + "ww3", + "ww4", + "wwww", + "your-domain-name" ] diff --git a/utils/lib/cpanel.js b/utils/lib/cpanel.js index e61b15a0f..573a22e5e 100644 --- a/utils/lib/cpanel.js +++ b/utils/lib/cpanel.js @@ -1,100 +1,123 @@ -const R = require('ramda'); -const qs = require('querystring'); -const { DOMAIN_API_HOST, DOMAIN_API_PORT, DOMAIN_USER, DOMAIN_API_KEY, DOMAIN_DOMAIN } = require('../constants'); +const R = require("ramda"); +const qs = require("querystring"); +const { + DOMAIN_API_HOST, + DOMAIN_API_PORT, + DOMAIN_USER, + DOMAIN_API_KEY, + DOMAIN_DOMAIN, +} = require("../constants"); const CpanelClient = (options) => { - const api = ({ basePath = '', action = '' }) => (module, func, defaultQuery = {}) => (q = {}) => { - const query = { - ...defaultQuery, - ...q, - cpanel_jsonapi_user: options.username, - cpanel_jsonapi_module: module, - cpanel_jsonapi_func: func, - cpanel_jsonapi_apiversion: 2, + const api = + ({ basePath = "", action = "" }) => + (module, func, defaultQuery = {}) => + (q = {}) => { + const query = { + ...defaultQuery, + ...q, + cpanel_jsonapi_user: options.username, + cpanel_jsonapi_module: module, + cpanel_jsonapi_func: func, + cpanel_jsonapi_apiversion: 2, + }; + + const request = { + headers: { + Authorization: `cpanel ${options.username}:${options.apiKey}`, + }, + rejectUnauthorized: false, + }; + + const path = `${basePath}/${action}?${qs.stringify(query)}`; + const reqUrl = `https://${options.host}:${options.port}/${path}`; + + const { fetch } = options.dependencies; + return fetch(reqUrl, request).then((res) => res.json()); + }; + + const api2 = api({ basePath: "json-api", action: "cpanel" }); + const uapi = (module, func, defaultQuery) => + api({ basePath: "execute", action: `${module}/${func}` })( + module, + func, + defaultQuery, + ); + + return { + zone: { + // { customonly, domain } + // -> [{ class, ttl, name, line, Line, cname, type, record }] + fetch: R.compose( + (p) => p.then(R.pathOr([], ["cpanelresult", "data"])), + api2("ZoneEdit", "fetchzone_records", { + customonly: 0, + domain: options.domain, + }), + ), + + // { name, type(A|CNAME), cname, address, ttl } + // -> {} + add: api2("ZoneEdit", "add_zone_record", { + domain: options.domain, + }), + + // { line } + // -> {} + remove: api2("ZoneEdit", "remove_zone_record", { + domain: options.domain, + }), + }, + redirection: { + // {} + // -> [{ domain, destination }] + fetch: R.compose( + (p) => p.then(R.pathOr([], ["data"])), + uapi("Mime", "list_redirects"), + ), + + // { domain, redirect, type(permanent|tmp), redirect_wildcard(0|1), redirect(0|1|2) } + // -> {} + add: uapi("Mime", "add_redirect"), + + // { domain } + // -> {} + remove: uapi("Mime", "delete_redirect"), + }, + file: { + write: uapi("Fileman", "save_file_content", { + from_charset: "UTF-8", + to_charset: "UTF-8", + fallback: 1, + }), + }, + email: { + // { domain, exchanger, priority } + // -> {} + add: uapi("Email", "add_mx", { alwaysaccept: "auto" }), + + // { domain, exchanger, priority } + // -> {} + remove: uapi("Email", "delete_mx", { alwaysaccept: "auto" }), + }, }; - - const request = { - headers: { - Authorization: `cpanel ${options.username}:${options.apiKey}`, - }, - rejectUnauthorized: false, - }; - - const path = `${basePath}/${action}?${qs.stringify(query)}`; - const reqUrl = `https://${options.host}:${options.port}/${path}`; - - const { fetch } = options.dependencies; - return fetch(reqUrl, request).then(res => res.json()); - }; - - const api2 = api({ basePath: 'json-api', action: 'cpanel' }); - const uapi = (module, func, defaultQuery) => - api({ basePath: 'execute', action: `${module}/${func}` })(module, func, defaultQuery); - - return { - zone: { - // { customonly, domain } - // -> [{ class, ttl, name, line, Line, cname, type, record }] - fetch: R.compose( - p => p.then(R.pathOr([], ['cpanelresult', 'data'])), - api2('ZoneEdit', 'fetchzone_records', { customonly: 0, domain: options.domain }) - ), - - // { name, type(A|CNAME), cname, address, ttl } - // -> {} - add: api2('ZoneEdit', 'add_zone_record', { domain: options.domain }), - - // { line } - // -> {} - remove: api2('ZoneEdit', 'remove_zone_record', { domain: options.domain }), - }, - redirection: { - // {} - // -> [{ domain, destination }] - fetch: R.compose( - p => p.then(R.pathOr([], ['data'])), - uapi('Mime', 'list_redirects'), - ), - - // { domain, redirect, type(permanent|tmp), redirect_wildcard(0|1), redirect(0|1|2) } - // -> {} - add: uapi('Mime', 'add_redirect'), - - // { domain } - // -> {} - remove: uapi('Mime', 'delete_redirect'), - }, - file: { - write: uapi('Fileman', 'save_file_content', { from_charset: 'UTF-8', to_charset: 'UTF-8', fallback: 1 }), - }, - email: { - // { domain, exchanger, priority } - // -> {} - add: uapi('Email', 'add_mx', { alwaysaccept: 'auto' }), - - // { domain, exchanger, priority } - // -> {} - remove: uapi('Email', 'delete_mx', { alwaysaccept: 'auto' }), - }, - }; }; if (!DOMAIN_API_KEY) { - console.error('Api key cannot be empty'); - process.exit(1); + console.error("Api key cannot be empty"); + process.exit(1); } const cpanel = CpanelClient({ - host: DOMAIN_API_HOST, - port: DOMAIN_API_PORT, - username: DOMAIN_USER, - apiKey: DOMAIN_API_KEY, - domain: DOMAIN_DOMAIN, - dependencies: { fetch }, + host: DOMAIN_API_HOST, + port: DOMAIN_API_PORT, + username: DOMAIN_USER, + apiKey: DOMAIN_API_KEY, + domain: DOMAIN_DOMAIN, + dependencies: { fetch }, }); module.exports = { - cpanel, - CpanelClient, + cpanel, + CpanelClient, }; - diff --git a/utils/validations.js b/utils/validations.js index d68f6a546..2facda7fa 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -1,102 +1,127 @@ -const R = require('ramda'); -const { VALID_RECORD_TYPES } = require('./constants'); -const { or, and, validate, between, testRegex, withLengthEq, withLengthGte } = require('./helpers'); -const INVALID_NAMES = require('./invalid-domains.json'); -const ipRegex_ = require('ip-regex'); +const R = require("ramda"); +const { VALID_RECORD_TYPES } = require("./constants"); +const { + or, + and, + validate, + between, + testRegex, + withLengthEq, + withLengthGte, +} = require("./helpers"); +const INVALID_NAMES = require("./invalid-domains.json"); +const ipRegex_ = require("ip-regex"); const ipRegex = ipRegex_.default ?? ipRegex_; -const isValidURL = and([R.is(String), testRegex(/^https?:\/\//ig)]); +const isValidURL = and([R.is(String), testRegex(/^https?:\/\//gi)]); -const isValidDomain = and([R.is(String), testRegex(/^(([a-z0-9-]+)\.)+[a-z]+$/ig)]); - -const validateCnameRecord = type => and([ - R.propIs(String, type), - R.compose(withLengthEq(1), R.keys), // CNAME cannot be used with any other record - R.propSatisfies(withLengthGte(4), type), - R.propSatisfies(isValidDomain, type), +const isValidDomain = and([ + R.is(String), + testRegex(/^(([a-z0-9-]+)\.)+[a-z]+$/gi), ]); -const validateARecord = type => and([ - R.propIs(Array, type), - R.propSatisfies(withLengthGte(1), type), - R.all(testRegex(ipRegex.v4({ exact: true }))), -]); +const validateCnameRecord = (type) => + and([ + R.propIs(String, type), + R.compose(withLengthEq(1), R.keys), // CNAME cannot be used with any other record + R.propSatisfies(withLengthGte(4), type), + R.propSatisfies(isValidDomain, type), + ]); -const validateMXRecord = type => and([ - R.propIs(Array, type), - R.propSatisfies(withLengthGte(1), type), - R.propSatisfies(R.all(isValidDomain), type), -]); +const validateARecord = (type) => + and([ + R.propIs(Array, type), + R.propSatisfies(withLengthGte(1), type), + R.all(testRegex(ipRegex.v4({ exact: true }))), + ]); -const validateAAAARecord = R.propSatisfies(and([ - R.is(Array), - withLengthGte(1), - R.all(testRegex(ipRegex.v6({ exact: true }))), -])) +const validateMXRecord = (type) => + and([ + R.propIs(Array, type), + R.propSatisfies(withLengthGte(1), type), + R.propSatisfies(R.all(isValidDomain), type), + ]); -const checkRestrictedNames = R.complement(R.includes(R.__, INVALID_NAMES)) +const validateAAAARecord = R.propSatisfies( + and([ + R.is(Array), + withLengthGte(1), + R.all(testRegex(ipRegex.v6({ exact: true }))), + ]), +); + +const checkRestrictedNames = R.complement(R.includes(R.__, INVALID_NAMES)); const extraSupportedNames = [ - testRegex(/^_github(-pages)?-challenge-[a-z0-9-_]+$/i), // Exception for github verification records - R.equals('_discord'), - R.equals('_gitlab-pages-verification-code'), - R.equals('_acme-challenge'), - R.equals('_dmarc'), - R.equals('_domainkey'), - testRegex(/^_gh-[a-z0-9-_]+$/i), // Exception for the new github org verification records -] + testRegex(/^_github(-pages)?-challenge-[a-z0-9-_]+$/i), + R.equals("_discord"), + R.equals("_gitlab-pages-verification-code"), + R.equals("_acme-challenge"), + R.equals("_dmarc"), + R.equals("_domainkey"), + testRegex(/^_gh-[a-z0-9-_]+$/i), +]; const validateDomainData = validate({ - name: { - reason: 'The name of the file is invalid. It must be lowercased, alphanumeric and each component must be more than 2 characters long', - fn: or([ - R.equals('@'), - and([ - R.is(String), - checkRestrictedNames, - R.compose( - R.all(or([ + name: { + reason: "The name of the file is invalid. It must be lowercased, alphanumeric and each component must be between 1-100 characters long.", + fn: or([ + R.equals("@"), and([ - R.compose(between(2, 100), R.length), - testRegex(/^[a-z0-9-]+$/g), - checkRestrictedNames, + R.is(String), + checkRestrictedNames, + R.compose( + R.all( + or([ + and([ + R.compose(between(1, 100), R.length), + testRegex(/^[a-z0-9-]+$/g), + checkRestrictedNames, + ]), + ...extraSupportedNames, + ]), + ), + R.split("."), + ), ]), - ...extraSupportedNames, - ])), - R.split('.'), - ), - ]) - ]), - }, - description: { reason: '', fn: R.T, }, - repo: { reason: '', fn: R.T, }, - owner: { - reason: '`owner` needs valid username and email properties', - fn: and([ - R.is(Object), - R.complement(R.isEmpty), - R.where({ - username: and([R.is(String), withLengthGte(1)]), - email: R.is(String), - }), - ]), - }, - record: { - reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', - fn: and([ - R.is(Object), - R.compose(R.isEmpty, R.difference(R.__, VALID_RECORD_TYPES), R.keys), - R.cond([ - [R.has('CNAME'), validateCnameRecord('CNAME')], - [R.has('A'), validateARecord('A')], - [R.has('URL'), R.propSatisfies(isValidURL, 'URL')], - [R.has('MX'), validateMXRecord('MX')], - [R.has('TXT'), R.propSatisfies(or([ R.is(String), R.is(Array) ]), 'TXT')], - [R.has('AAAA'), validateAAAARecord('AAAA')], - [R.T, R.T], - ]), - ]), - }, + ]), + }, + description: { reason: "", fn: R.T }, + repo: { reason: "", fn: R.T }, + owner: { + reason: "`owner` key needs valid username and email properties.", + fn: and([ + R.is(Object), + R.complement(R.isEmpty), + R.where({ + username: and([R.is(String), withLengthGte(1)]), + email: R.is(String), + }), + ]), + }, + record: { + reason: "Invalid record(s) found. Please check the record types and values.", + fn: and([ + R.is(Object), + R.compose( + R.isEmpty, + R.difference(R.__, VALID_RECORD_TYPES), + R.keys, + ), + R.cond([ + [R.has("CNAME"), validateCnameRecord("CNAME")], + [R.has("A"), validateARecord("A")], + [R.has("URL"), R.propSatisfies(isValidURL, "URL")], + [R.has("MX"), validateMXRecord("MX")], + [ + R.has("TXT"), + R.propSatisfies(or([R.is(String), R.is(Array)]), "TXT"), + ], + [R.has("AAAA"), validateAAAARecord("AAAA")], + [R.T, R.T], + ]), + ]), + }, }); module.exports = { validateDomainData, isValidDomain }; From 197c2f08a2dd538de5e4f63cc0cc19758e93b824 Mon Sep 17 00:00:00 2001 From: William Harrison Date: Mon, 29 Jul 2024 08:39:36 +0800 Subject: [PATCH 2/2] Update validations.test.js --- tests/validations.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/validations.test.js b/tests/validations.test.js index 6f4c85daf..224e244aa 100644 --- a/tests/validations.test.js +++ b/tests/validations.test.js @@ -73,7 +73,6 @@ describe("validateDomainData", () => { 0, 1, ), - { ...defaultDomain, name: "a.b" }, { ...defaultDomain, name: "ww2.baa" }, { ...defaultDomain, name: "help.baa" }, { ...defaultDomain, name: "_github-pages-challenge-is-a-dev" }, @@ -142,6 +141,7 @@ describe("validateDomainData", () => { { ...defaultDomain, name: "_gh-hello01_ga" }, { ...defaultDomain, name: "_domainkey.subdomain" }, { ...defaultDomain, name: "mx._domainkey.subdomain" }, + { ...defaultDomain, name: "a.b" }, ]; it("should return false for invalid data", () => {