'use strict' let request = require('@root/request') request = require('util').promisify(request) const Joi = require('@hapi/joi') const schema = Joi.object().keys({ authKey: Joi.string().alphanum().required(), authEmail: Joi.string().email({ minDomainSegments: 2 }).required(), baseUrl: Joi.string() }).with('username', 'birthyear').without('password', 'access_token') function formatError (msg, resp) { const e = new Error(`${resp.statusCode}: ${msg}! (Check the Credentials)`) e.body = resp.body if (resp.body && resp.body.errors) { e.errors = resp.body.errors console.log(e.errors[0].error_chain) } e.statusCode = resp.statusCode throw e } var defaults = { baseUrl: 'https://api.cloudflare.com/client/v4/' } module.exports.create = function (config) { const baseUrl = (config.baseUrl || defaults.baseUrl).replace(/\/$/, '') Joi.validate(config, schema) function api (method, path, body) { return request({ method: method, url: baseUrl + path, headers: { 'Content-Type': 'application/json', 'X-Auth-Key': config.authKey, 'X-Auth-Email': config.authEmail }, json: true, body }) } async function zones (domain) { const resp = await api('GET', '/zones?per_page=1000' + (domain ? '&name=' + domain : '')) // TODO: use proper pagination?! if (resp.statusCode !== 200) { formatError('Could not get list of Zones', resp) } return resp } async function getZone (domain) { const res = await zones(domain) return res.body.result.filter(zone => (zone.name === domain))[0] } return { zones: async function (data) { const resp = await zones() return resp.body.result.map(x => x.name) }, set: async function (data) { const { challenge: { dnsZone: domain, dnsAuthorization: txtRecord, dnsPrefix } } = data const zone = await getZone(domain) if (zone.permissions.indexOf('#zone:edit') === -1) { throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account') } const resp = await api('POST', `/zones/${zone.id}/dns_records`, {type: 'TXT', name: dnsPrefix, content: JSON.stringify(txtRecord), ttl: 300}) if (resp.statusCode !== 200) { formatError('Could not add record', resp) } return true }, remove: async function (data) { const { challenge: { dnsZone: domain, dnsPrefix } } = data const zone = await getZone(domain) if (zone.permissions.indexOf('#zone:edit') === -1) { throw new Error('Can not edit zone ' + JSON.stringify(domain) + ' from this account') } const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`) if (resp.statusCode !== 200) { formatError('Could not read record', resp) } let {result} = resp.body let record = result.filter(record => (record.type === 'TXT'))[0] if (record) { const resp = await api('DELETE', `/zones/${zone.id}/dns_records/${record.id}`) if (resp.statusCode !== 200) { formatError('Could not delete record', resp) } } else { return null // TODO: not found. should this throw?! } }, get: async function (data) { const { challenge: { dnsZone: domain, dnsPrefix } } = data const zone = await getZone(domain) if (zone.permissions.indexOf('#zone:read') === -1) { throw new Error('Can not read zone ' + JSON.stringify(domain) + ' from this account') } const resp = await api('GET', `/zones/${zone.id}/dns_records?name=${encodeURI(dnsPrefix + '.' + domain)}`) if (resp.statusCode !== 200) { formatError('Could not read record', resp) } let {result} = resp.body let record = result.filter(record => (record.type === 'TXT'))[0] if (record) { return {dnsAuthorization: JSON.parse(record.content)} } else { return null // TODO: not found. should this throw?! } } } }