From 92c44c1ad8849df0b2959d52cb66c2b67be79ee5 Mon Sep 17 00:00:00 2001 From: andrewstech Date: Wed, 16 Oct 2024 09:39:09 +0000 Subject: [PATCH] New CI --- .env.example | 6 - .envrc | 1 - .github/workflows/checks.yml | 18 -- .github/workflows/publish-records.yml | 30 -- .github/workflows/publish.yml | 32 ++ .github/workflows/validate.yml | 43 +++ Dockerfile | 22 -- bun.lockb | Bin 51000 -> 0 bytes default.nix | 15 - dnsconfig.js | 128 ++++++++ docker-compose.yml | 8 - package.json | 27 -- scripts/action-utils.js | 8 - scripts/certbot-auth.sh | 17 -- scripts/certbot.sh | 91 ------ scripts/register-domains.js | 76 ----- scripts/reply.js | 48 --- scripts/verify-record.sh | 11 - tests/cpanel.test.js | 210 ------------- tests/domain-service.test.js | 421 -------------------------- tests/domain-utils.test.js | 8 - tests/domains.test.js | 37 --- tests/register.test.js | 242 --------------- tests/validations.test.js | 167 ---------- utils/constants.js | 30 -- utils/domain-service.js | 206 ------------- utils/get-domain.js | 27 -- utils/helpers.js | 54 ---- utils/invalid-domains.json | 27 -- utils/lib/cpanel.js | 123 -------- utils/validations.js | 129 -------- 31 files changed, 203 insertions(+), 2059 deletions(-) delete mode 100644 .env.example delete mode 100644 .envrc delete mode 100644 .github/workflows/checks.yml delete mode 100644 .github/workflows/publish-records.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/validate.yml delete mode 100644 Dockerfile delete mode 100755 bun.lockb delete mode 100644 default.nix create mode 100644 dnsconfig.js delete mode 100644 docker-compose.yml delete mode 100644 package.json delete mode 100644 scripts/action-utils.js delete mode 100755 scripts/certbot-auth.sh delete mode 100755 scripts/certbot.sh delete mode 100644 scripts/register-domains.js delete mode 100644 scripts/reply.js delete mode 100644 scripts/verify-record.sh delete mode 100644 tests/cpanel.test.js delete mode 100644 tests/domain-service.test.js delete mode 100644 tests/domain-utils.test.js delete mode 100644 tests/domains.test.js delete mode 100644 tests/register.test.js delete mode 100644 tests/validations.test.js delete mode 100644 utils/constants.js delete mode 100644 utils/domain-service.js delete mode 100644 utils/get-domain.js delete mode 100644 utils/helpers.js delete mode 100644 utils/invalid-domains.json delete mode 100644 utils/lib/cpanel.js delete mode 100644 utils/validations.js diff --git a/.env.example b/.env.example deleted file mode 100644 index 8022d3f4e..000000000 --- a/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -DOMAIN_USER=username -DOMAIN_API_KEY=apikey -DOMAIN_API_HOST=api-example.com -DOMAIN_API_PORT=2087 -DOMAIN_DOMAIN=example.com -DOMAIN_HOST_IP=69.69.69.69 diff --git a/.envrc b/.envrc deleted file mode 100644 index 4a4726a5c..000000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use_nix diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index be5f73e48..000000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Checks - -on: [pull_request, workflow_dispatch] - -jobs: - validation: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - run: bun install - - name: Run tests - run: bun test diff --git a/.github/workflows/publish-records.yml b/.github/workflows/publish-records.yml deleted file mode 100644 index 40e58b3ea..000000000 --- a/.github/workflows/publish-records.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Publish records - -on: - push: - branches: [main] - workflow_dispatch: - -jobs: - publish: - if: github.repository == 'is-a-dev/register' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - run: bun install - - run: bun test - - name: Publishing records - env: - CI: 1 - NODE_ENV: production - ENV: production - DOMAIN_USER: ${{ secrets.DOMAIN_USER }} - DOMAIN_API_KEY: ${{ secrets.DOMAIN_API_KEY }} - DOMAIN_API_HOST: ${{ secrets.DOMAIN_API_HOST }} - DOMAIN_API_PORT: ${{ secrets.DOMAIN_API_PORT }} - DOMAIN_DOMAIN: ${{ secrets.DOMAIN_DOMAIN }} - DOMAIN_HOST_IP: ${{ secrets.DOMAIN_HOST_IP }} - run: bun run publish-records diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..8577075f3 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,32 @@ +name: Publish + +on: + push: + branches: [main] + paths: + - "domains/**" + - ".github/workflows/publish.yml" + - "dnsconfig.js" + + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-publish + cancel-in-progress: false + +jobs: + dns: + name: DNS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - run: echo '{"cloudflare":{"TYPE":"CLOUDFLAREAPI","apitoken":"$CLOUDFLARE_API_TOKEN"}}' > ./creds.json + + - name: Publish + uses: koenrh/dnscontrol-action@v3 + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} + with: + args: push + config_file: "dnsconfig.js" \ No newline at end of file diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 000000000..b49211b69 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,43 @@ +name: Validation + +on: + pull_request: + + push: + branches: [main] + paths: + - "domains/**" + - ".github/workflows/validation.yml" + - "dnsconfig.js" + + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-validation + cancel-in-progress: true + +jobs: + dns: + name: DNS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check + uses: koenrh/dnscontrol-action@v3 + with: + args: check + config_file: "dnsconfig.js" + + json: + name: JSON + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: JSON Syntax Check + uses: limitusus/json-syntax-check@v2 + with: + pattern: "\\.json$" + env: + BASE: "domains/" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 990a6e0b2..000000000 --- a/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM ubuntu:20.04 - -ENV TERM xterm -RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime - -RUN apt-get -y update -RUN apt-get install -y nodejs npm curl wget dnsutils certbot --fix-missing -RUN apt-get install -y unzip - -RUN bash -c "curl -fsSL https://bun.sh/install | bash -s 'bun-v1.0.15'" - -RUN ~/.bun/bin/bun -v - -WORKDIR /opt/app - -COPY bun.lockb . -COPY package.json . - -RUN ~/.bun/bin/bun install - -CMD ["sh", "-c", "cp -r node_modules code; cd code; tail -f /dev/null"] - diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 5022120107e0be77c9cb0067f2b55c8b7f2a579d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51000 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 tltRQHB3=-6Vhw6{0xR_vbne {} }: -let - inherit (nixpkgs) pkgs; - - nixPackages = with pkgs; [ - yarn - docker-compose - dnsutils - bun - ]; -in -pkgs.stdenv.mkDerivation { - name = "env"; - buildInputs = nixPackages; -} diff --git a/dnsconfig.js b/dnsconfig.js new file mode 100644 index 000000000..f053b8b4b --- /dev/null +++ b/dnsconfig.js @@ -0,0 +1,128 @@ +var regNone = NewRegistrar("none"); +var providerCf = DnsProvider(NewDnsProvider("cloudflare")); + +var proxy = { + on: { "cloudflare_proxy": "on" }, + off: { "cloudflare_proxy": "off" } +}; + +// Function to validate filenames according to the given rules +function isValidFilename(filename) { + var regex = /^[a-z0-9]+(-[a-z0-9]+)*(\.[a-z0-9]+(-[a-z0-9]+)*)*\.json$/; + return regex.test(filename); +} + +function getDomainsList(filesPath) { + var result = []; + var files = glob.apply(null, [filesPath, true, '.json']); + + for (var i = 0; i < files.length; i++) { + var basename = files[i].split('/').reverse()[0]; + + if (!isValidFilename(basename)) { + console.log(`Skipping invalid file: ${basename}`); + continue; + } + + var name = basename.split('.')[0]; + result.push({ name: name, data: require(files[i]) }); + } + + return result; +} + +var domains = getDomainsList('./domains'); +var commit = {}; + +for (var idx in domains) { + var domainData = domains[idx].data; + var proxyState = proxy.off; // disabled by default + + if (!commit[domainData.record.domain]) { + commit[domainData.record.domain] = []; + } + + if (domainData.record.proxied === false) { + proxyState = proxy.off; + } + + // Handle CNAME record + if (domainData.record.CNAME) { + commit[domainData.record.domain].push( + CNAME(domainData.subdomain, domainData.record.CNAME + ".", proxyState) + ); + } + + // Handle A records + if (domainData.record.A) { + for (var a in domainData.record.A) { + commit[domainData.record.domain].push( + A(domainData.subdomain, IP(domainData.record.A[a]), proxyState) + ); + } + } + + // Handle AAAA records + if (domainData.record.AAAA) { + for (var aaaa in domainData.record.AAAA) { + commit[domainData.record.domain].push( + AAAA(domainData.subdomain, domainData.record.AAAA[aaaa], proxyState) + ); + } + } + + // Handle MX records + if (domainData.record.MX) { + for (var mx in domainData.record.MX) { + commit[domainData.record.domain].push( + MX(domainData.subdomain, 10, domainData.record.MX[mx] + ".") + ); + } + } + + // Handle URL records (redirect) + if (domainData.record.URL) { + commit[domainData.record.domain].push( + URL(domainData.subdomain, domainData.record.URL) + ); + } + + // Handle NS records + if (domainData.record.NS) { + for (var ns in domainData.record.NS) { + commit[domainData.domain].push( + NS(domainData.subdomain, domainData.record.NS[ns] + ".") + ); + } + } + + // Handle SRV records + if (domainData.record.SRV) { + for (var srv in domainData.record.SRV) { + var srvRecord = domainData.record.SRV[srv]; + commit[domainData.domain].push( + SRV(domainData.subdomain, srvRecord.priority, srvRecord.weight, srvRecord.port, srvRecord.target + ".") + ); + } + } + + // Handle TXT records + if (domainData.record.TXT) { + if (Array.isArray(domainData.record.TXT)) { + for (var txt in domainData.record.TXT) { + commit[domainData.record.domain].push( + TXT(domainData.subdomain, domainData.record.TXT[txt]) + ); + } + } else { + commit[domainData.record.domain].push( + TXT(domainData.subdomain, domainData.record.TXT) + ); + } + } +} + +// Commit all DNS records +for (var domainName in commit) { + D(domainName, regNone, providerCf, commit[domainName]); +} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5b161f929..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3' -services: - dev: - build: - context: . - dockerfile: ./Dockerfile - volumes: - - ./:/opt/app/code diff --git a/package.json b/package.json deleted file mode 100644 index ccc8dbd41..000000000 --- a/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "is-a-dev-core", - "version": "1.0.0", - "description": "Register *.is-a.dev domains for free", - "scripts": { - "lint": "eslint utils scripts domains --ext .json,.js", - "publish-records": "bun run -b ./scripts/register-domains.js", - "dc": "docker-compose -p is-a-dev", - "dc:start": "bun run dc up", - "dc:shell": "bun run dc run dev /bin/bash" - }, - "repository": { - "type": "git", - "url": "https://github.com/is-a-dev/register" - }, - "author": "Akshay Nair ", - "license": "GPL-3.0", - "dependencies": { - "ip-regex": "^5.0.0", - "ramda": "^0.27.1" - }, - "devDependencies": { - "eslint": "^7.11.0", - "eslint-plugin-json": "^2.1.2", - "eslint-plugin-node": "^11.1.0" - } -} diff --git a/scripts/action-utils.js b/scripts/action-utils.js deleted file mode 100644 index 38a33a420..000000000 --- a/scripts/action-utils.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - hasLabel: (context, label) => { - const pr = context.payload.pull_request || context.payload.issue; - const { labels = [] } = pr; - - return !!labels.find(({ name }) => name === label); - }, -}; diff --git a/scripts/certbot-auth.sh b/scripts/certbot-auth.sh deleted file mode 100755 index 6b4ee8bc7..000000000 --- a/scripts/certbot-auth.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -echo "$PWD"; - -echo "AUTH ::[$CERTBOT_VALIDATION]::[$CERTBOT_TOKEN]::[$CERTBOT_REMAINING_CHALLENGES]"; -echo "[$CERTBOT_DOMAIN]"; - -sleep 1; -./scripts/certbot.sh acme_txt "$CERTBOT_VALIDATION"; - -echo "Going to sleep for a few minutes..."; - -# TODO: Check if $CERTBOT_VALIDATION == $(./scripts/certbot.sh get-acme)? -sleep $((3*60)); - -./scripts/certbot.sh check; - diff --git a/scripts/certbot.sh b/scripts/certbot.sh deleted file mode 100755 index ee84aa6d7..000000000 --- a/scripts/certbot.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash - -export NODE_ENV=production -DRY_RUN=0 -if_dry_run() { [[ $DRY_RUN == 1 ]] && echo "$1" || echo "$2"; } - -generate_certificate() { - outdir=$(mktemp -d /tmp/is-a-dev-certbot.XXXXX); - - certbot \ - --config-dir $outdir/config \ - --work-dir $outdir/work \ - --logs-dir $outdir/logs \ - certonly \ - --manual \ - --preferred-challenges=dns \ - --manual-auth-hook=./scripts/certbot-auth.sh \ - -m 'phenax5@gmail.com' \ - -d '*.is-a.dev,is-a.dev' \ - --agree-tos \ - $(if_dry_run "--dry-run" ""); - - echo "+-----------------------------------------------+"; - echo "| Certificate output: |"; - echo "|= $outdir"; - echo "+-----------------------------------------------+"; -} - -update_record() { - local method=$([[ "$1" == "add" ]] && echo "addZoneRecord" || echo "removeZoneRecord"); - local type="$2"; - local name="$3"; - local address="$4"; - local ttl=${5:-"1"}; - bun -e " - const { domainService } = require('./utils/domain-service'); - const { ENV, DOMAIN_DOMAIN } = require('./utils/constants'); - const method = '$method'; - const name = '$name'; - const type = '$type'; - - const record = { name, type, address: '$address', ttl: $ttl }; - - async function main() { - if (method === 'removeZoneRecord') { - const data = await domainService.get({ customonly: 0, name: '$name.is-a.dev.', type }); - if (data.length > 0) { - record.id = data[0].line; - } else { - throw new Error('Unable to find record'); - } - } - - console.log('Uploading $name to', DOMAIN_DOMAIN, '(', ENV, ')...'); - const data = await domainService[method](record)(); - console.log(data.cpanelresult ? data.cpanelresult.data : data); - } - - main().catch(console.error); - " -} - -update_acme_txt_record() { - update_record add TXT '_acme-challenge' "$1"; -} - -reset_acme() { - sleep 1; - update_record remove TXT '_acme-challenge' ''; - update_record remove TXT '_acme-challenge' ''; - # update_record add CNAME 'www' "is-a-dev.github.io"; -} - -get_acme() { dig +noall +answer _acme-challenge.is-a.dev TXT | awk '{print $5}'; } - -case "$1" in - check) echo "TXT record:: $(get_acme)" ;; - get-acme) get_acme ;; - cert) generate_certificate ;; - acme_txt) update_acme_txt_record "$2" ;; - reset) reset_acme ;; - *) echo "Invalid command"; exit 1; ;; -esac - - -### STEPS ### -# Run ./scripts/certbot.sh cert -# cp -r /tmp/is-a-dev-whatever /opt/app/code/is-a-dev-cert -# Upload cert.pem and privkey.pem (from config/live/is-a.dev/) contents to SSL > Manage SSL Sites -# Run ./scripts/certbot.sh reset - diff --git a/scripts/register-domains.js b/scripts/register-domains.js deleted file mode 100644 index 94bc02089..000000000 --- a/scripts/register-domains.js +++ /dev/null @@ -1,76 +0,0 @@ -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, - "", - ); -}; - -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); - - 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); -}); - -const registerDomains = async ({ - domainService, - getDomains, - log = () => {}, -}) => { - const domains = await getDomains().then(toHostList); - - if (domains.length === 0) - return Promise.reject(new Error("Nothing to register")); - - 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); -}; - -if (require.main === module) { - main().catch((e) => { - console.error(e); - process.exit(1); - }); -} else { - module.exports = { toHostList, registerDomains }; -} diff --git a/scripts/reply.js b/scripts/reply.js deleted file mode 100644 index dd4df142d..000000000 --- a/scripts/reply.js +++ /dev/null @@ -1,48 +0,0 @@ -const getInstructions = () => ` -The changes have been published!! It should reflect in less than 24 hours. - -## Here's what you need to do next -If your domain points to a server you own, add \`domain-name.is-a.dev\` to your server config. For https, you will have to configure ssl certificate to allow the new subdomain. - -### For github pages users, -* Go to your github page repo (\`user/user.github.io\`) -* Open up the **settings** tab -* Scroll down to the **Github pages** section -* In the **Custom domain** text input, enter the domain you registered (\`domain-name.is-a.dev\`) -* Check the **Enforce HTTPS** checkbox below the input -* Give it some time to reflect and you should be good to go - - -## Need help with your domain? -If you are having trouble setting up your domain, [create an issue](https://github.com/is-a-dev/register/issues/new/choose). I will try my best to get back to you asap! - - -## Made a mistake in the record? -Don't worry, you can create a new pull request with the corrections - - -## Love/Hate the service? -**Love it?** Leave it a **star**! Also consider donating so that I can keep this service running forever! -**Hate it?** Please leave your feedback by [creating an issue](https://github.com/is-a-dev/register/issues/new/choose). I would really like to keep improving this service for other users. - - -## Wanna support this project? -Help me in my mission to keep this service alive forever by donating! - -Buy Me A Coffee Liberapay recurring donation button - -`; - -module.exports = { - 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(), - }); - }, -}; diff --git a/scripts/verify-record.sh b/scripts/verify-record.sh deleted file mode 100644 index a4fc0da6a..000000000 --- a/scripts/verify-record.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env sh - -sed \ - -e 's/\.json.*$/.is-a.dev/g' \ - -e 's/^\s*domains\///g' \ - -e '/^\s*$/d' \ -| while read domain; do - echo "$domain"; - dig "$domain" +noall +answer && echo "done" || echo "x"; -done; - diff --git a/tests/cpanel.test.js b/tests/cpanel.test.js deleted file mode 100644 index b2415f8b4..000000000 --- a/tests/cpanel.test.js +++ /dev/null @@ -1,210 +0,0 @@ -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 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" }); - }); - }); - - 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, - }); - }); - }); - - 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, - }); - }); - }); - - 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(); - }); - }); - 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, - }); - }); - - 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 deleted file mode 100644 index dfd6400ed..000000000 --- a/tests/domain-service.test.js +++ /dev/null @@ -1,421 +0,0 @@ -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), - }, -}); - -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" }], - }); - }); - - 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" }], - }); - }); - - 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" }], - }); - }); - - 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" }, - ], - }); - }); -}); - -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 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" }, - ]); - }); - - 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(); - - 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", - }, - ]); - }); - }); - - 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: "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); - }); - - 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}` }, - ]); - }); - }); -}); diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js deleted file mode 100644 index 896cf017d..000000000 --- a/tests/domain-utils.test.js +++ /dev/null @@ -1,8 +0,0 @@ -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); - }); -}); diff --git a/tests/domains.test.js b/tests/domains.test.js deleted file mode 100644 index 75971b00b..000000000 --- a/tests/domains.test.js +++ /dev/null @@ -1,37 +0,0 @@ -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); - }); - - 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 deleted file mode 100644 index 6628df211..000000000 --- a/tests/register.test.js +++ /dev/null @@ -1,242 +0,0 @@ -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: { - 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"] }, - }, - ]); - - 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 () => ({})); - - 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(); - }); - - 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, - }); - - 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" }, - ]; - - 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(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(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 deleted file mode 100644 index af27b68bb..000000000 --- a/tests/validations.test.js +++ /dev/null @@ -1,167 +0,0 @@ -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", - }, -}; - -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 }, - ]; - - 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: "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" }, - { ...defaultDomain, name: "_improvmx" }, - { ...defaultDomain, name: "_vercel" }, - ]; - - 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" }, - { ...defaultDomain, name: '_improvmx.subdomain' }, - { ...defaultDomain, name: '_vercel.subdomain' }, - { ...defaultDomain, name: "a.b" }, - ]; - - 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 deleted file mode 100644 index b0cf0079d..000000000 --- a/utils/constants.js +++ /dev/null @@ -1,30 +0,0 @@ -const path = require("path"); - -const { NODE_ENV: ENV = "test" } = process.env; - -const { - DOMAIN_USER, - DOMAIN_API_KEY, - DOMAIN_DOMAIN, - DOMAIN_API_HOST, - DOMAIN_API_PORT, - DOMAIN_HOST_IP, -} = process.env; - -const IS_TEST = ENV === "test"; - -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, -}; diff --git a/utils/domain-service.js b/utils/domain-service.js deleted file mode 100644 index b76eeac01..000000000 --- a/utils/domain-service.js +++ /dev/null @@ -1,206 +0,0 @@ -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, -}); -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 } : {}), -}); - -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, -}) => ({ - id, - name: cleanName(name), - type: `${type}`, - 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, ""), -}); - -const recordToEmailMx = ({ name, address, priority }) => ({ - domain: `${name}.is-a.dev`, - exchanger: address, - priority, -}); - -const getHostKey = (host) => - `${host.name.toLowerCase()}##${host.type.toLowerCase()}##${host.address.toLowerCase()}`; - -const isReserved = (domain) => - domain.name.startsWith("*") || !VALID_RECORD_TYPES.includes(domain.type); - -const diffRecords = (oldRecords, newRecords) => { - const isMatchingRecord = (a, b) => getHostKey(a) === getHostKey(b); - - const remove = R.differenceWith(isMatchingRecord, oldRecords, newRecords); - const add = R.differenceWith(isMatchingRecord, newRecords, oldRecords); - - 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 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); - - log(`${values.length - failed.length}/${values.length}`); - failed.length && log(JSON.stringify(failed, null, 2)); - - 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 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 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}`); - - await executeBatch([...removeRecords(remove), ...addRecords(add)]); - return { added: add.length, removed: remove.length }; - }; - - return { - getHosts, - get: cpanel.zone.fetch, - addZoneRecord, - removeZoneRecord, - updateHosts, - }; -}; - -const domainService = getDomainService({ cpanel }); - -module.exports = { - getDomainService, - domainService, - diffRecords, -}; diff --git a/utils/get-domain.js b/utils/get-domain.js deleted file mode 100644 index 064240358..000000000 --- a/utils/get-domain.js +++ /dev/null @@ -1,27 +0,0 @@ -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 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 getDomains = () => - fs.promises.readdir(DOMAINS_PATH, {}).then( - R.map((name) => ({ - ...toDomainData(name), - name: name.replace(/\.json$/, ""), - })), - ); - -module.exports = { getDomains }; diff --git a/utils/helpers.js b/utils/helpers.js deleted file mode 100644 index 03369f2b7..000000000 --- a/utils/helpers.js +++ /dev/null @@ -1,54 +0,0 @@ -const R = require("ramda"); -const { IS_TEST } = require("./constants"); - -const log = IS_TEST ? () => {} : console.log; -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 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 lazyTask = (fn) => (data) => () => fn(data); - -const batchLazyTasks = (count) => (tasks) => - tasks.reduce((batches, task) => { - if (batches.length === 0) return [[task]]; - - const full = R.init(batches); - const last = R.last(batches); - - 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); - -module.exports = { - or, - and, - validate, - between, - testRegex, - log, - print, - then, - lazyTask, - batchLazyTasks, - withLengthEq, - withLengthGte, -}; diff --git a/utils/invalid-domains.json b/utils/invalid-domains.json deleted file mode 100644 index 736faa9bd..000000000 --- a/utils/invalid-domains.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - "0", - "_acme-challenge", - "_discord", - "_dmarc", - "_domainkey", - "_gh-is-a-dev", - "_github-challenge-is-a-dev", - "_github-pages-challenge-is-a-dev", - "_gitlab-pages-verification-code", - "_improvmx", - "_vercel", - "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 deleted file mode 100644 index 573a22e5e..000000000 --- a/utils/lib/cpanel.js +++ /dev/null @@ -1,123 +0,0 @@ -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 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); -} - -const cpanel = CpanelClient({ - host: DOMAIN_API_HOST, - port: DOMAIN_API_PORT, - username: DOMAIN_USER, - apiKey: DOMAIN_API_KEY, - domain: DOMAIN_DOMAIN, - dependencies: { fetch }, -}); - -module.exports = { - cpanel, - CpanelClient, -}; diff --git a/utils/validations.js b/utils/validations.js deleted file mode 100644 index eea099cd4..000000000 --- a/utils/validations.js +++ /dev/null @@ -1,129 +0,0 @@ -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?:\/\//gi)]); - -const isValidDomain = and([ - R.is(String), - testRegex(/^(([a-z0-9-_]+)\.)*(([a-z0-9-]+)\.)+[a-z]+$/gi), -]); - -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 validateARecord = (type) => - and([ - R.propIs(Array, type), - R.propSatisfies(withLengthGte(1), type), - R.all(testRegex(ipRegex.v4({ exact: true }))), - ]); - -const validateMXRecord = (type) => - and([ - R.propIs(Array, type), - R.propSatisfies(withLengthGte(1), type), - R.propSatisfies(R.all(isValidDomain), type), - ]); - -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), - R.equals("_discord"), - R.equals("_gitlab-pages-verification-code"), - R.equals("_acme-challenge"), - R.equals("_dmarc"), - R.equals("_domainkey"), - R.equals("_improvmx"), - R.equals("_vercel"), - 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 between 1-100 characters long.", - fn: or([ - R.equals("@"), - and([ - 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("."), - ), - ]), - ]), - }, - 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 };