@firstdorsal/powerdns-api
Advanced tools
Comparing version 1.7.7 to 1.8.0
926
index.js
@@ -1,17 +0,16 @@ | ||
'use strict'; | ||
const f = require("node-fetch") | ||
"use strict"; | ||
const f = require("node-fetch"); | ||
const secondLevelRegex = new RegExp(/[A-Z-a-z0-9]{1,63}\.[A-Z-a-z0-9]{1,63}$/); | ||
/** | ||
/** | ||
* @typedef Cryptokey | ||
* @type {object} | ||
* @prop {'ksk'|'zsk'|'csk'} keytype The type of the key possible values are | ||
* @prop {boolean} active Whether or not the key is in active use | ||
* @prop {boolean} published Whether or not the DNSKEY record is published in the zone | ||
* @prop {string} dnskey The DNSKEY record for this key | ||
* @prop {string} privatekey The private key in ISC format | ||
* @type {object} | ||
* @prop {'ksk'|'zsk'|'csk'} keytype The type of the key possible values are | ||
* @prop {boolean} active Whether or not the key is in active use | ||
* @prop {boolean} published Whether or not the DNSKEY record is published in the zone | ||
* @prop {string} dnskey The DNSKEY record for this key | ||
* @prop {string} privatekey The private key in ISC format | ||
* @prop {string} algorithm The name of the algorithm of the key, should be a mnemonic | ||
*/ | ||
/** | ||
/** | ||
* @typedef Search | ||
@@ -55,4 +54,2 @@ * @type {object} | ||
/** @module powerdns-api */ | ||
@@ -74,46 +71,45 @@ /** @class Class representing the powerdns client | ||
module.exports.PowerdnsClient = class { | ||
/** | ||
* Create a powerdns client. | ||
* @constructor | ||
* @param {string} baseurl The base url where the api can be found | ||
* @param {string} apikey The api key for the powerdns endpoint | ||
*/ | ||
constructor(baseurl, apikey) { | ||
this.baseurl = baseurl; | ||
this.apikey = apikey; | ||
} | ||
/** | ||
* Takes domain name as string. Returns the domain name as string in absolute form with a . at the end. example.com -> example.com. and example.com. -> example.com. | ||
* @private | ||
* @param {string} name takes a domain name | ||
* @returns {string} the domain name in absolute form ending with a dot | ||
*/ | ||
absoluteName(name) { | ||
if (name[name.length - 1] !== '.') return name + '.'; | ||
return name; | ||
} | ||
/** | ||
* Create a powerdns client. | ||
* @constructor | ||
* @param {string} baseurl The base url where the api can be found | ||
* @param {string} apikey The api key for the powerdns endpoint | ||
*/ | ||
constructor(baseurl, apikey) { | ||
this.baseurl = baseurl; | ||
this.apikey = apikey; | ||
} | ||
/** | ||
* Takes domain name as string. Returns the domain name as string in absolute form with a . at the end. example.com -> example.com. and example.com. -> example.com. | ||
* @private | ||
* @param {string} name takes a domain name | ||
* @returns {string} the domain name in absolute form ending with a dot | ||
*/ | ||
absoluteName(name) { | ||
if (name[name.length - 1] !== ".") return name + "."; | ||
return name; | ||
} | ||
/** | ||
* takes array of records and sorts its contents by name into multiple arrays | ||
* @private | ||
* @param {Array} records array of records | ||
* @returns {Array} array of arrays with records with the same name | ||
* */ | ||
sortRecordsByDomainName(records) { | ||
const result = []; | ||
for (let i = 0; i < records.length; i++) { | ||
let p = false; | ||
for (let j = 0; j < result.length; j++) { | ||
if (records[i].name === result[j][0].name) { | ||
result[j].push(records[i]); | ||
p = true; | ||
break; | ||
} | ||
} | ||
if (!p) result.push([records[i]]); | ||
/** | ||
* takes array of records and sorts its contents by name into multiple arrays | ||
* @private | ||
* @param {Array} records array of records | ||
* @returns {Array} array of arrays with records with the same name | ||
* */ | ||
sortRecordsByDomainName(records) { | ||
const result = []; | ||
for (let i = 0; i < records.length; i++) { | ||
let p = false; | ||
for (let j = 0; j < result.length; j++) { | ||
if (records[i].name === result[j][0].name) { | ||
result[j].push(records[i]); | ||
p = true; | ||
break; | ||
} | ||
return result; | ||
} | ||
if (!p) result.push([records[i]]); | ||
} | ||
/** | ||
return result; | ||
} | ||
/** | ||
* Returns array of all zones from this pdns server. | ||
@@ -125,14 +121,14 @@ * @async | ||
*/ | ||
getZones() { | ||
return f(this.baseurl + '/zones', { | ||
method: 'GET', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
json: true | ||
}).then((res) => { | ||
return res.json(); | ||
}); | ||
} | ||
/** | ||
getZones() { | ||
return f(this.baseurl + "/zones", { | ||
method: "GET", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
json: true, | ||
}).then((res) => { | ||
return res.json(); | ||
}); | ||
} | ||
/** | ||
* Creates zone/domain and returns its SOA record on success. | ||
@@ -147,29 +143,33 @@ | ||
*/ | ||
createZone(zoneName, kind = 'Native') { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones`, { | ||
method: 'POST', | ||
headers: { | ||
'X-Api-Key': this.apikey, | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify({ | ||
name: zoneNameSan + '.', | ||
kind | ||
}) | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
if (!j.rrsets) return false; | ||
return j.rrsets[0] | ||
}).catch((err) => { | ||
throw new Error(err); | ||
}); | ||
} | ||
/** | ||
createZone(zoneName, kind = "Native") { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones`, { | ||
method: "POST", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
name: zoneNameSan + ".", | ||
kind, | ||
}), | ||
}) | ||
.then(async (res) => { | ||
let j = await res.text().catch(); | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
if (!j.rrsets) return false; | ||
return j.rrsets[0]; | ||
}) | ||
.catch((err) => { | ||
throw new Error(err); | ||
}); | ||
} | ||
/** | ||
* Returns single zone with meta information. | ||
@@ -182,15 +182,15 @@ * @async | ||
*/ | ||
getZoneWithMeta(zoneName) { | ||
zoneName = this.absoluteName(zoneName); | ||
return f(this.baseurl + '/zones/' + zoneName, { | ||
method: 'GET', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
json: true | ||
}).then((res) => { | ||
return res.json(); | ||
}); | ||
} | ||
/** | ||
getZoneWithMeta(zoneName) { | ||
zoneName = this.absoluteName(zoneName); | ||
return f(this.baseurl + "/zones/" + zoneName, { | ||
method: "GET", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
json: true, | ||
}).then((res) => { | ||
return res.json(); | ||
}); | ||
} | ||
/** | ||
* Returns array with rrsets for zone. | ||
@@ -203,19 +203,23 @@ * @async | ||
*/ | ||
getZone(zoneName) { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
return f(this.baseurl + '/zones/' + zoneNameSan, { | ||
method: 'GET', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
json: true | ||
}).then((res) => { | ||
return res.json().catch(() => {}); | ||
}).then((json) => { | ||
if (json && json.rrsets) return json.rrsets; | ||
return null; | ||
}); | ||
} | ||
/** | ||
getZone(zoneName) { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(this.baseurl + "/zones/" + zoneNameSan, { | ||
method: "GET", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
json: true, | ||
}) | ||
.then((res) => { | ||
return res.json().catch(() => {}); | ||
}) | ||
.then((json) => { | ||
if (json && json.rrsets) return json.rrsets; | ||
return null; | ||
}); | ||
} | ||
/** | ||
* Deletes the whole zone with all attached metadata and rrsets. | ||
@@ -228,21 +232,25 @@ * @async | ||
*/ | ||
deleteZone(zoneName) { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
return f(this.baseurl + '/zones/' + zoneNameSan, { | ||
method: 'DELETE', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
json: true | ||
}).then(async (res) => { | ||
let j = await res.text(); | ||
deleteZone(zoneName) { | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(this.baseurl + "/zones/" + zoneNameSan, { | ||
method: "DELETE", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
json: true, | ||
}) | ||
.then(async (res) => { | ||
let j = await res.text(); | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}).catch((err) => { | ||
throw err; | ||
}) | ||
} | ||
/** | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}) | ||
.catch((err) => { | ||
throw err; | ||
}); | ||
} | ||
/** | ||
* Takes records for a SINGLE domain as array and sets them. If records exist it replaces them. | ||
@@ -260,55 +268,58 @@ * @async | ||
*/ | ||
setHomogeneousRecords(records) { | ||
if (!Array.isArray(records)) throw new TypeError('Parameter must be of type array'); | ||
setHomogeneousRecords(records) { | ||
if (!Array.isArray(records)) | ||
throw new TypeError("Parameter must be of type array"); | ||
const dname = this.absoluteName(records[0].name); | ||
const zoneName = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
let rrsets = []; | ||
for (let i = 0; i < records.length; i++) { | ||
let recordsOut = []; | ||
for (let j = 0; j < records[i].content.length; j++) { | ||
const dname = this.absoluteName(records[0].name); | ||
const zoneName = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
let rrsets = []; | ||
for (let i = 0; i < records.length; i++) { | ||
let recordsOut = []; | ||
for (let j = 0; j < records[i].content.length; j++) { | ||
recordsOut.push({ | ||
content: records[i].content[j], | ||
disabled: false, | ||
ttl: typeof records[i].ttl == "undefined" ? 3600 : records[i].ttl, | ||
name: this.absoluteName(records[i].name), | ||
type: typeof records[i].type == "undefined" ? "A" : records[i].type, | ||
}); | ||
} | ||
rrsets.push({ | ||
name: this.absoluteName(records[i].name), | ||
type: typeof records[i].type == "undefined" ? "A" : records[i].type, | ||
ttl: typeof records[i].ttl == "undefined" ? 3600 : records[i].ttl, | ||
changetype: "REPLACE", | ||
records: recordsOut, | ||
}); | ||
} | ||
return f(this.baseurl + "/zones/" + zoneName, { | ||
method: "PATCH", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
body: JSON.stringify({ | ||
rrsets, | ||
}), | ||
}) | ||
.then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j.includes("error")) console.log(j); | ||
recordsOut.push({ | ||
"content": records[i].content[j], | ||
"disabled": false, | ||
"ttl": typeof (records[i].ttl) == 'undefined' ? 3600 : records[i].ttl, | ||
"name": this.absoluteName(records[i].name), | ||
"type": typeof (records[i].type) == 'undefined' ? 'A' : records[i].type | ||
}); | ||
} | ||
rrsets.push({ | ||
"name": this.absoluteName(records[i].name), | ||
"type": typeof (records[i].type) == 'undefined' ? 'A' : records[i].type, | ||
"ttl": typeof (records[i].ttl) == 'undefined' ? 3600 : records[i].ttl, | ||
"changetype": "REPLACE", | ||
records: recordsOut | ||
}); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
return f(this.baseurl + '/zones/' + zoneName, { | ||
method: 'PATCH', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
body: JSON.stringify({ | ||
rrsets | ||
}) | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j.includes('error')) console.log(j); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}).catch((err) => { | ||
throw err; | ||
}); | ||
} | ||
/** | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}) | ||
.catch((err) => { | ||
throw err; | ||
}); | ||
} | ||
/** | ||
* Takes records as array and deletes them. | ||
@@ -324,40 +335,45 @@ * @async | ||
*/ | ||
deleteRecords(records) { | ||
if (!Array.isArray(records)) throw new TypeError('Parameter must be of type array'); | ||
const dname = this.absoluteName(records[0].name); | ||
const zoneName = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
deleteRecords(records) { | ||
if (!Array.isArray(records)) | ||
throw new TypeError("Parameter must be of type array"); | ||
const dname = this.absoluteName(records[0].name); | ||
const zoneName = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
let rrsets = []; | ||
for (let i = 0; i < records.length; i++) { | ||
rrsets.push({ | ||
"name": this.absoluteName(records[i].name), | ||
"type": records[i].type, | ||
"changetype": "DELETE", | ||
}); | ||
let rrsets = []; | ||
for (let i = 0; i < records.length; i++) { | ||
rrsets.push({ | ||
name: this.absoluteName(records[i].name), | ||
type: records[i].type, | ||
changetype: "DELETE", | ||
}); | ||
} | ||
return f(this.baseurl + "/zones/" + zoneName, { | ||
method: "PATCH", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
body: JSON.stringify({ | ||
rrsets, | ||
}), | ||
}) | ||
.then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
return f(this.baseurl + '/zones/' + zoneName, { | ||
method: 'PATCH', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
body: JSON.stringify({ | ||
rrsets | ||
}) | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}).catch((err) => { | ||
throw err; | ||
}); | ||
} | ||
/** | ||
if (j === undefined || j.length === 0) return true; | ||
return false; | ||
}) | ||
.catch((err) => { | ||
throw err; | ||
}); | ||
} | ||
/** | ||
* Takes Search object and searches for matching elements in the pdns server. | ||
@@ -374,23 +390,27 @@ * @async | ||
*/ | ||
search(search = {}) { | ||
if (!search.max) search.max = 10; | ||
if (!search.object_type) search.object_type = 'record'; | ||
if (!search.query) return null; | ||
return f(`${this.baseurl}/search-data?q=${search.query}&max=${search.max}&object_type=${search.object_type}`, { | ||
method: 'GET', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
json: true | ||
}).then((res) => { | ||
return res.json().catch((err) => { | ||
console.log(err); | ||
}); | ||
}).then((json) => { | ||
if (json) return json; | ||
return null; | ||
search(search = {}) { | ||
if (!search.max) search.max = 10; | ||
if (!search.object_type) search.object_type = "record"; | ||
if (!search.query) return null; | ||
return f( | ||
`${this.baseurl}/search-data?q=${search.query}&max=${search.max}&object_type=${search.object_type}`, | ||
{ | ||
method: "GET", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
json: true, | ||
} | ||
) | ||
.then((res) => { | ||
return res.json().catch((err) => { | ||
console.log(err); | ||
}); | ||
} | ||
/** | ||
}) | ||
.then((json) => { | ||
if (json) return json; | ||
return null; | ||
}); | ||
} | ||
/** | ||
* Takes ONE record as object and appends it not replacing other records with the same name. | ||
@@ -408,14 +428,14 @@ * @async | ||
*/ | ||
async appendRecord(record) { | ||
const a = await this.search({ | ||
query: record.name | ||
}); | ||
if (a) { | ||
for (let i = 0; i < a.length; i++) { | ||
if (a[i] && a[i].type !== 'PTR') record.content.push(a[i].content); | ||
} | ||
} | ||
return await this.setRecords([record]); | ||
async appendRecord(record) { | ||
const a = await this.search({ | ||
query: record.name, | ||
}); | ||
if (a) { | ||
for (let i = 0; i < a.length; i++) { | ||
if (a[i] && a[i].type !== "PTR") record.content.push(a[i].content); | ||
} | ||
} | ||
/** | ||
return await this.setRecords([record]); | ||
} | ||
/** | ||
* Creates a DNS Cryptokey and enables it for DNSSEC. If you want to import your own please read the original [documentation]{@link https://doc.powerdns.com/authoritative/http-api/cryptokey.html} and put it in the Cryptokey parameter. | ||
@@ -430,32 +450,104 @@ * @async | ||
*/ | ||
createCryptokey(zoneName, cryptokey = { | ||
keytype: "ksk", | ||
active: true | ||
}, returnPrivateKey = false) { | ||
if (!zoneName) throw new Error('Missing zone/domain name'); | ||
if (!cryptokey.keytype) throw new Error('Missing keytype'); | ||
createCryptokey( | ||
zoneName, | ||
cryptokey = { | ||
keytype: "ksk", | ||
active: true, | ||
}, | ||
returnPrivateKey = false | ||
) { | ||
if (!zoneName) throw new Error("Missing zone/domain name"); | ||
if (!cryptokey.keytype) throw new Error("Missing keytype"); | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname.substr(0, dname.length - 1).match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones/${zoneNameSan}/cryptokeys`, { | ||
method: 'POST', | ||
headers: { | ||
'X-Api-Key': this.apikey | ||
}, | ||
body: JSON.stringify(cryptokey) | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
if (returnPrivateKey) return j; | ||
delete j.privatekey; | ||
return j; | ||
}) | ||
} | ||
/** | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones/${zoneNameSan}/cryptokeys`, { | ||
method: "POST", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
body: JSON.stringify(cryptokey), | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
if (returnPrivateKey) return j; | ||
delete j.privatekey; | ||
return j; | ||
}); | ||
} | ||
/** | ||
* Get the crypotkeys for a given zone. | ||
* @async | ||
* @param {string} zoneName name of the zone/domain | ||
* @returns {Array.<Cryptokey>} on success the cryptokeys of the zone will be returned | ||
* @example | ||
await pdns.getCryptoKeys("example.com"); | ||
*/ | ||
getCryptoKeys(zoneName) { | ||
if (!zoneName) throw new Error("Missing zone/domain name"); | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones/${zoneNameSan}/cryptokeys`, { | ||
method: "GET", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw j; | ||
} | ||
} | ||
return j; | ||
}); | ||
} | ||
/** | ||
* Delete a cryptokey with specified id from specified zone. | ||
* @async | ||
* @param {string} zoneName name of the zone/domain | ||
* @param {string} cryptokeyId id of the cryptokey | ||
* @returns {boolean} true on success | ||
* @example | ||
await pdns.deleteCryptoKey("example.com",171); | ||
*/ | ||
deleteCryptoKey(zoneName, cryptokeyId) { | ||
if (!zoneName) throw new Error("Missing zone/domain name"); | ||
const dname = this.absoluteName(zoneName); | ||
const zoneNameSan = dname | ||
.substr(0, dname.length - 1) | ||
.match(secondLevelRegex)[0]; | ||
return f(`${this.baseurl}/zones/${zoneNameSan}/cryptokeys/${cryptokeyId}`, { | ||
method: "DELETE", | ||
headers: { | ||
"X-Api-Key": this.apikey, | ||
}, | ||
}).then(async (res) => { | ||
let j = await res.text().catch(); | ||
if (j) { | ||
try { | ||
j = JSON.parse(j); | ||
} catch (err) { | ||
throw Error(j); | ||
} | ||
} | ||
return j === "" ? true : j; | ||
}); | ||
} | ||
/** | ||
* Takes records for single or mixed domains as array and sets them. If records exist it replaces them. | ||
@@ -483,13 +575,12 @@ * @async | ||
*/ | ||
async setRecords(records) { | ||
records = this.sortRecordsByDomainName(records); | ||
let ir = []; | ||
for (let i = 0; i < records.length; i++) { | ||
ir.push(this.setHomogeneousRecords(records[i])) | ||
} | ||
async setRecords(records) { | ||
records = this.sortRecordsByDomainName(records); | ||
let ir = []; | ||
for (let i = 0; i < records.length; i++) { | ||
ir.push(this.setHomogeneousRecords(records[i])); | ||
} | ||
return await Promise.all(ir); | ||
} | ||
/** | ||
return await Promise.all(ir); | ||
} | ||
/** | ||
* Searches for records in a zone by comparing the RECORDS field NOT the name field. Replaces the found records with the replace string. | ||
@@ -504,31 +595,31 @@ * @async | ||
*/ | ||
async replaceRecords(find, replace, zone) { | ||
const toReplace = []; | ||
const zoneSets = await this.getZone(zone) | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
const content = []; | ||
let foundOne = false; | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
content.push(replace) | ||
foundOne = true; | ||
} else { | ||
content.push(zoneSets[j].records[k].content); | ||
} | ||
} | ||
if (foundOne) { | ||
toReplace.push({ | ||
name: zoneSets[j].name, | ||
type: zoneSets[j].type, | ||
ttl: zoneSets[j].ttl, | ||
content, | ||
}); | ||
} | ||
} | ||
async replaceRecords(find, replace, zone) { | ||
const toReplace = []; | ||
const zoneSets = await this.getZone(zone); | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
const content = []; | ||
let foundOne = false; | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
content.push(replace); | ||
foundOne = true; | ||
} else { | ||
content.push(zoneSets[j].records[k].content); | ||
} | ||
} | ||
await this.setRecords(toReplace); | ||
return toReplace.length | ||
if (foundOne) { | ||
toReplace.push({ | ||
name: zoneSets[j].name, | ||
type: zoneSets[j].type, | ||
ttl: zoneSets[j].ttl, | ||
content, | ||
}); | ||
} | ||
} | ||
} | ||
/** | ||
await this.setRecords(toReplace); | ||
return toReplace.length; | ||
} | ||
/** | ||
* Searches for records on the pdns server by comparing the RECORDS field NOT the name field. Replaces the found records with the replace string. | ||
@@ -542,34 +633,34 @@ * @async | ||
*/ | ||
async replaceRecordsGlobal(find, replace) { | ||
const allZones = await this.getZones(); | ||
const toReplace = []; | ||
for (let i = 0; i < allZones.length; i++) { | ||
const zoneSets = await this.getZone(allZones[i].name) | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
const content = []; | ||
let foundOne = false; | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
content.push(replace) | ||
foundOne = true; | ||
} else { | ||
content.push(zoneSets[j].records[k].content); | ||
} | ||
} | ||
if (foundOne) { | ||
toReplace.push({ | ||
name: zoneSets[j].name, | ||
type: zoneSets[j].type, | ||
ttl: zoneSets[j].ttl, | ||
content, | ||
}); | ||
} | ||
} | ||
async replaceRecordsGlobal(find, replace) { | ||
const allZones = await this.getZones(); | ||
const toReplace = []; | ||
for (let i = 0; i < allZones.length; i++) { | ||
const zoneSets = await this.getZone(allZones[i].name); | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
const content = []; | ||
let foundOne = false; | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
content.push(replace); | ||
foundOne = true; | ||
} else { | ||
content.push(zoneSets[j].records[k].content); | ||
} | ||
} | ||
if (foundOne) { | ||
toReplace.push({ | ||
name: zoneSets[j].name, | ||
type: zoneSets[j].type, | ||
ttl: zoneSets[j].ttl, | ||
content, | ||
}); | ||
} | ||
} | ||
await this.setRecords(toReplace); | ||
return toReplace.length | ||
} | ||
} | ||
/** | ||
await this.setRecords(toReplace); | ||
return toReplace.length; | ||
} | ||
/** | ||
* Searches for records in a zone by comparing the RECORDS field NOT the name field | ||
@@ -583,17 +674,17 @@ * @async | ||
*/ | ||
async findRecords(find, zone) { | ||
const res = []; | ||
const zoneSets = await this.getZone(zone) | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
res.push(zoneSets[j]); | ||
} | ||
} | ||
} | ||
async findRecords(find, zone) { | ||
const res = []; | ||
const zoneSets = await this.getZone(zone); | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
res.push(zoneSets[j]); | ||
} | ||
} | ||
return res; | ||
} | ||
} | ||
/** | ||
return res; | ||
} | ||
/** | ||
* Searches for records on the pdns server by comparing the RECORDS field NOT the name field | ||
@@ -606,24 +697,24 @@ * @async | ||
*/ | ||
async findRecordsGlobal(find) { | ||
const allZones = await this.getZones(); | ||
const res = []; | ||
for (let i = 0; i < allZones.length; i++) { | ||
const zoneSets = await this.getZone(allZones[i].name) | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
res.push(zoneSets[j]); | ||
} | ||
} | ||
} | ||
async findRecordsGlobal(find) { | ||
const allZones = await this.getZones(); | ||
const res = []; | ||
for (let i = 0; i < allZones.length; i++) { | ||
const zoneSets = await this.getZone(allZones[i].name); | ||
if (zoneSets) { | ||
for (let j = 0; j < zoneSets.length; j++) { | ||
for (let k = 0; k < zoneSets[j].records.length; k++) { | ||
if (zoneSets[j].records[k].content === find) { | ||
res.push(zoneSets[j]); | ||
} | ||
} | ||
} | ||
return res; | ||
} | ||
} | ||
/** | ||
* Higher level function for creating a zone with a custom soa record, name servers and dnssec | ||
return res; | ||
} | ||
/** | ||
* Higher level function for creating a zone with a custom soa record, name servers and dnssec/cryptokey. Skips creating zone and/or cryptokey if it exists. | ||
* @async | ||
* @param {Object} zone string to search for | ||
* @returns {Boolean} success on true | ||
* @returns {Boolean} true on success | ||
* @example | ||
@@ -637,22 +728,53 @@ await pdns.createAndSetupZone({ | ||
*/ | ||
async createAndSetupZone(zone) { | ||
await this.createZone(zone.domain).catch(e => { | ||
if (e.toString().includes('Conflict')) console.log('domain already exists: skipping creation') | ||
}); | ||
await this.setRecords([{ | ||
name: zone.domain.match(secondLevelRegex)[0], | ||
type: "SOA", | ||
ttl: 3600, | ||
content: [`${this.absoluteName(zone.nameserver[0])} ${zone.hostmasterEmail.replace('@','.')}. 2020111501 10800 3600 604800 3600`] | ||
}, { | ||
name: zone.domain.match(secondLevelRegex)[0], | ||
type: "NS", | ||
ttl: 3600, | ||
content: zone.nameserver.map(e => this.absoluteName(e)) | ||
}]).catch(e => { | ||
console.log(e) | ||
}); | ||
return await this.createCryptokey(zone.domain); | ||
async createAndSetupZone(zone) { | ||
const zor = ` | ||
Your function should look something like this: | ||
await pdns.createAndSetupZone({ | ||
domain: 'example.com', | ||
nameserver: ['ns1.paulisttoll.somedomain', 'ns2.paulisttoll.somedomain', 'ns3.paulisttoll.somedomain'], | ||
hostmasterEmail:'hostmaster@paulisttoll.somedomain' | ||
}); | ||
`; | ||
if (!zone) throw Error("No zone object provided" + zor); | ||
if (!zone.domain) throw Error("No domain specified" + zor); | ||
if (!zone.nameserver) throw Error("No nameserver provided" + zor); | ||
if (!zone.nameserver.length) | ||
throw Error("Your zone needs to have at least one nameserver" + zor); | ||
if (!zone.hostmasterEmail) throw Error("No hostmasterEmail provided" + zor); | ||
await this.createZone(zone.domain).catch((e) => { | ||
if (e.toString().includes("Conflict")) | ||
console.log("domain already exists: skipping creation"); | ||
}); | ||
await this.setRecords([ | ||
{ | ||
name: zone.domain.match(secondLevelRegex)[0], | ||
type: "SOA", | ||
ttl: 3600, | ||
content: [ | ||
`${this.absoluteName( | ||
zone.nameserver[0] | ||
)} ${zone.hostmasterEmail.replace( | ||
"@", | ||
"." | ||
)}. 2020111501 10800 3600 604800 3600`, | ||
], | ||
}, | ||
{ | ||
name: zone.domain.match(secondLevelRegex)[0], | ||
type: "NS", | ||
ttl: 3600, | ||
content: zone.nameserver.map((e) => this.absoluteName(e)), | ||
}, | ||
]).catch((e) => { | ||
console.log(e); | ||
}); | ||
const cryptokey = await this.getCryptoKeys(zone.domain); | ||
if (!cryptokey || !cryptokey.length) { | ||
return await this.createCryptokey(zone.domain); | ||
} | ||
} | ||
console.log("Cryptokey exists: skipping creation"); | ||
return true; | ||
} | ||
}; |
{ | ||
"name": "@firstdorsal/powerdns-api", | ||
"version": "1.7.7", | ||
"version": "1.8.0", | ||
"description": "A Nodejs client for the PowerDns API with the most relevant functions.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -112,2 +112,4 @@ # Powerdns API | ||
* [.createCryptokey(zoneName, [cryptokey], [returnPrivateKey])](#module_powerdns-api.PowerdnsClient+createCryptokey) ⇒ <code>Object</code> | ||
* [.getCryptoKeys(zoneName)](#module_powerdns-api.PowerdnsClient+getCryptoKeys) ⇒ [<code>Array.<Cryptokey></code>](#Cryptokey) | ||
* [.deleteCryptoKey(zoneName, cryptokeyId)](#module_powerdns-api.PowerdnsClient+deleteCryptoKey) ⇒ <code>boolean</code> | ||
* [.setRecords(records)](#module_powerdns-api.PowerdnsClient+setRecords) ⇒ <code>Array</code> | ||
@@ -139,2 +141,4 @@ * [.replaceRecords(find, replace, zone)](#module_powerdns-api.PowerdnsClient+replaceRecords) ⇒ <code>Number</code> | ||
* [.createCryptokey(zoneName, [cryptokey], [returnPrivateKey])](#module_powerdns-api.PowerdnsClient+createCryptokey) ⇒ <code>Object</code> | ||
* [.getCryptoKeys(zoneName)](#module_powerdns-api.PowerdnsClient+getCryptoKeys) ⇒ [<code>Array.<Cryptokey></code>](#Cryptokey) | ||
* [.deleteCryptoKey(zoneName, cryptokeyId)](#module_powerdns-api.PowerdnsClient+deleteCryptoKey) ⇒ <code>boolean</code> | ||
* [.setRecords(records)](#module_powerdns-api.PowerdnsClient+setRecords) ⇒ <code>Array</code> | ||
@@ -347,2 +351,35 @@ * [.replaceRecords(find, replace, zone)](#module_powerdns-api.PowerdnsClient+replaceRecords) ⇒ <code>Number</code> | ||
``` | ||
<a name="module_powerdns-api.PowerdnsClient+getCryptoKeys"></a> | ||
#### powerdnsClient.getCryptoKeys(zoneName) ⇒ [<code>Array.<Cryptokey></code>](#Cryptokey) | ||
Get the crypotkeys for a given zone. | ||
**Kind**: instance method of [<code>PowerdnsClient</code>](#module_powerdns-api.PowerdnsClient) | ||
**Returns**: [<code>Array.<Cryptokey></code>](#Cryptokey) - on success the cryptokeys of the zone will be returned | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| zoneName | <code>string</code> | name of the zone/domain | | ||
**Example** | ||
```js | ||
await pdns.getCryptoKeys("example.com"); | ||
``` | ||
<a name="module_powerdns-api.PowerdnsClient+deleteCryptoKey"></a> | ||
#### powerdnsClient.deleteCryptoKey(zoneName, cryptokeyId) ⇒ <code>boolean</code> | ||
Delete a cryptokey with specified id from specified zone. | ||
**Kind**: instance method of [<code>PowerdnsClient</code>](#module_powerdns-api.PowerdnsClient) | ||
**Returns**: <code>boolean</code> - true on success | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| zoneName | <code>string</code> | name of the zone/domain | | ||
| cryptokeyId | <code>string</code> | id of the cryptokey | | ||
**Example** | ||
```js | ||
await pdns.deleteCryptoKey("example.com",171); | ||
``` | ||
<a name="module_powerdns-api.PowerdnsClient+setRecords"></a> | ||
@@ -450,6 +487,6 @@ | ||
#### powerdnsClient.createAndSetupZone(zone) ⇒ <code>Boolean</code> | ||
Higher level function for creating a zone with a custom soa record, name servers and dnssec | ||
Higher level function for creating a zone with a custom soa record, name servers and dnssec/cryptokey. Skips creating zone and/or cryptokey if it exists. | ||
**Kind**: instance method of [<code>PowerdnsClient</code>](#module_powerdns-api.PowerdnsClient) | ||
**Returns**: <code>Boolean</code> - success on true | ||
**Returns**: <code>Boolean</code> - true on success | ||
@@ -456,0 +493,0 @@ | Param | Type | Description | |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
48806
4
740
568