diff --git a/.env b/.env new file mode 100644 index 000000000..4dd9ee6b6 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +NC_USER=xxx +NC_API_KEY=xxxxxxxxxxxxxxx +NC_DOMAIN=domain.com diff --git a/.gitignore b/.gitignore index 552f22184..b4bfa3d64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ +*.env.* *.log diff --git a/package.json b/package.json index 340b4de11..97ab5cb37 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "author": "Akshay Nair ", "license": "GPL-3.0", "dependencies": { + "@rqt/namecheap": "^2.4.2", + "dotenv": "^8.2.0", "jest": "^26.4.2", "ramda": "^0.27.1" } diff --git a/scripts/register-domains.js b/scripts/register-domains.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/domain-service.test.js b/tests/domain-service.test.js new file mode 100644 index 000000000..982616f80 --- /dev/null +++ b/tests/domain-service.test.js @@ -0,0 +1,44 @@ +const R = require('ramda'); +const { getDomainService } = require('../utils/domain-service'); + +const getNcClass = ({ onSet, onGet } = {}) => class Namecheap { + dns = { + setHosts: (_, list) => onSet(list), + getHosts: (_) => onGet(), + }; +}; + +describe('Domain service', () => { + describe('getHosts', () => { + it('should resolve with a list of hosts', async () => { + const hosts = [ + { Name: 'xx', Type: 'CNAME', Address: 'fck.com.' }, + { Name: 'xx', Type: 'A', Address: '111.1.1212.1' }, + ]; + const onGet = async () => ({ hosts }) + const mockDomainService = getDomainService({ Namecheap: getNcClass({ onGet }) }); + const list = await mockDomainService.getHosts(); + + expect(list).toEqual([ + { HostName: 'xx', RecordType: 'CNAME', Address: 'fck.com' }, + { HostName: 'xx', RecordType: 'A', Address: '111.1.1212.1' }, + ]); + }); + }); + + describe('setHosts', () => { + it('should resolve with a list of hosts', async () => { + const records = [ { x: 'y' }, { z: 'a' } ]; + + const onSet = jest.fn((list) => { + expect(list).toBe(records); + return Promise.resolve(null); + }); + + const mockDomainService = getDomainService({ Namecheap: getNcClass({ onSet }) }); + await mockDomainService.setHosts(records); + expect(onSet).toBeCalledTimes(1); + }); + }); +}); + diff --git a/utils/domain-service.js b/utils/domain-service.js new file mode 100644 index 000000000..980b77047 --- /dev/null +++ b/utils/domain-service.js @@ -0,0 +1,85 @@ +const R = require('ramda'); +const Namecheap = require('@rqt/namecheap'); + +const path = require('path'); +require('dotenv').config({ path: path.resolve('.env.sandbox') }); + +const IS_SANDBOX = true; +const { NC_USER, NC_API_KEY, NC_DOMAIN } = process.env; +const TTL = 5*60; + +const getDomainService = ({ Namecheap }) => { + const nc = new Namecheap({ + user: NC_USER, + key: NC_API_KEY, + ip: '103.226.85.9', + sandbox: IS_SANDBOX, + }); + + let hostList = []; + + console.log(NC_USER, NC_DOMAIN, NC_API_KEY); + + const getHosts = async () => { + if (hostList.length) return hostList; + + const list = await nc.dns.getHosts(NC_DOMAIN) + .then(R.propOr([], 'hosts')) + .then(R.map(host => ({ + ...host, + HostName: host.Name, + RecordType: host.Type, + Address: `${host.Address}`.replace(/\.$/g, ''), + Name: undefined, + Type: undefined, + }))); + + //console.log(list); + hostList = list; + return list; + }; + + const setHosts = hosts => nc.dns.setHosts(NC_DOMAIN, hosts); + + const findHost = async host => { + const list = await getHosts(); + const matchIndex = list.findIndex(R.whereEq({ + RecordType: host.RecordType, + HostName: host.HostName, + Address: host.Address, + // MXPref: host.MXPref, + TTL: host.TTL || TTL, + })); + + return matchIndex; + }; + + const mergeHosts = async hosts => { + return hosts; + //const hostList = await getHosts(); + //hosts.map() + // If source is bigger, merge all matching items and add new ones + // If dest is bigger, merge all matching items and add missing ones + }; + + return { getHosts, setHosts, findHost }; +} + +module.exports = { + getDomainService, + domainService: getDomainService({ Namecheap }), +}; + +//getDomainService({ Namecheap }).setHosts([ + //{ HostName: 'fuck', RecordType: 'CNAME', Address: 'google.com', TTL }, + //{ HostName: 'fuck.booboo.xyz', RecordType: 'URL', Address: 'https://fuck.booboo.xyz' }, + //{ HostName: 'foobar', RecordType: 'CNAME', Address: 'duckduckgo.com', TTL }, + //{ HostName: 'hello', RecordType: 'A', Address: '103.130.211.123', TTL }, +//]).then(console.log).catch(console.error); + +//getDomainService({ Namecheap }).getHosts() + //.then(console.log) + //.catch(console.error); + +//getDomainService({ Namecheap }).hasHost({ HostName: 'fuck', RecordType: 'CNAME', Address: 'duckduckgo.com' }); + diff --git a/yarn.lock b/yarn.lock index 26d309b10..2cb029d01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -458,6 +458,11 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@rqt/namecheap@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@rqt/namecheap/-/namecheap-2.4.2.tgz#8537eea6efbe7ac4fd449e3aee5f2c1d80986fcf" + integrity sha512-RfrK7ywOraz0nR/BlQt8fJQDcOfI9cLSTpa6l9JS9ER0HJ6t1FQ8Pxmplj/lVQv8rkczuVADdZVC3HlL98Q31w== + "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" @@ -1141,6 +1146,11 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"