'use strict'; module.exports.create = function (opts) { // opts = { filepath }; var engine = { db: null }; var db = require(opts.filepath); var crypto = require('crypto'); db.primaryNameservers.forEach(function (ns) { if (!ns.id) { ns.id = crypto.randomBytes(16).toString('hex'); } }); db.zones.forEach(function (zone) { if (!zone.name) { zone.name = zone.id; zone.id = null; } if (!zone.id) { zone.id = crypto.randomBytes(16).toString('hex'); } }); db.records.forEach(function (record) { if (!record.id) { record.id = crypto.randomBytes(16).toString('hex'); } }); require('fs').writeFileSync(opts.filepath, JSON.stringify(db, null, 2)); engine.primaryNameservers = db.primaryNameservers; engine.zoneToSoa = function (domain) { var nameservers = domain.vanityNs || engine.primaryNameservers.map(function (n) { return n.name; }); var index = Math.floor(Math.random() * nameservers.length) % nameservers.length; var nameserver = nameservers[index]; return { name: domain.name , typeName: 'SOA' , className: 'IN' , ttl: domain.ttl || 60 // nameserver -- select an NS at random if they're all in sync , primary: nameserver , name_server: nameserver // admin -- email address or domain for admin , admin: domain.admin || ('admin.' + domain.name) , email_addr: domain.admin || ('admin.' + domain.name) // serial -- the version, for cache-busting of secondary nameservers. suggested format: YYYYMMDDnn , serial: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000) , sn: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000) // refresh -- only used when nameservers following the DNS NOTIFY spec talk , refresh: domain.refresh || 1800 , ref: domain.refresh || 1800 // retry -- only used when nameservers following the DNS NOTIFY spec talk , retry: domain.retry || 600 , ret: domain.retry || 600 // expiration -- how long other nameservers should continue when the primary goes down , expiration: domain.expiration || 2419200 , ex: domain.expiration || 2419200 // minimum -- how long to cache a non-existent domain (also the default ttl for BIND) , minimum: domain.minimum || 5 , nx: domain.minimum || 5 }; }; engine.peers = { all: function (cb) { process.nextTick(function () { cb(null, db.primaryNameservers); }); } }; engine.zones = { all: function (cb) { process.nextTick(function () { cb(null, db.zones.slice(0)); }); } , get: function (queries, cb) { if (!Array.isArray(queries)) { queries = queries.names.map(function (n) { return { name: n }; }); } var myDomains = db.zones.filter(function (d) { return queries.some(function (q) { return d.name.toLowerCase() === q.name; }); }); process.nextTick(function () { cb(null, myDomains); }); } }; engine.records = { all: function (cb) { process.nextTick(function () { cb(null, db.records.slice(0)); }); } , get: function (query, cb) { var myRecords = db.records.slice(0).filter(function (r) { if ('string' !== typeof r.name) { return false; } // TODO use IN in masterquest (or implement OR) // Only return single-level wildcard? if (query.name === r.name || ('*.' + query.name.split('.').slice(1).join('.')) === r.name) { return true; } }); process.nextTick(function () { cb(null, myRecords); }); } }; return engine; };