acme-dns-01-cloudflare.js/lib/index.js

147 lines
4.1 KiB
JavaScript

'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?!
}
}
}
}