| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  | var crypto = require('crypto'); | 
					
						
							|  |  |  | var dns = require('dns'); | 
					
						
							|  |  |  | var os = require('os'); | 
					
						
							|  |  |  | var path = require('path'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var pathResolvers = { | 
					
						
							|  |  |  |   '.': function fromCwd(relPath) { | 
					
						
							|  |  |  |     return path.join(process.cwd(), relPath); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   '~': function fromHomedir(relPath) { | 
					
						
							|  |  |  |     if (!os.homedir) { | 
					
						
							|  |  |  |       throw new Error( | 
					
						
							|  |  |  |         'Resolving home directory relative paths is not supported in this version of node.' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return path.join(os.homedir(), relPath); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   noop: function (p) { return p; } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  | module.exports.create = function (opts) { | 
					
						
							|  |  |  |   // opts = { filepath };
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |   // `opts.filepath` is a module id or path to a module that contains a store plugin or file
 | 
					
						
							|  |  |  |   var pathFn = pathResolvers[opts.filepath[0]] || pathResolvers.noop; | 
					
						
							|  |  |  |   var storeId = pathFn(opts.filepath); | 
					
						
							|  |  |  |   var pathToStore = require.resolve(storeId); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  |   var engine = { db: null }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 14:54:03 -07:00
										 |  |  |   function notDeleted(r) { | 
					
						
							|  |  |  |     return !r.revokedAt && !r.deletedAt; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |   // instantiate the DB module
 | 
					
						
							|  |  |  |   var db = (pathToStore.slice(-5) === '.json') ? | 
					
						
							|  |  |  |     // JSON files should be loaded using our built in store.json.js
 | 
					
						
							|  |  |  |     require('./store.json.js')(pathToStore) : | 
					
						
							|  |  |  |     // everything else should be loaded as a module and passed our opts object
 | 
					
						
							|  |  |  |     require(storeId)(opts); | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |   // TODO: examine usage of engine.primaryNameservers to see if we are supporting it right
 | 
					
						
							|  |  |  |   engine.primaryNameservers = db.primaryNameservers.list; | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 14:54:08 -07:00
										 |  |  |   engine.peers = { | 
					
						
							|  |  |  |     all: function (cb) { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       var pNS = db.primaryNameservers.list(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function getRecord(ns, done) { | 
					
						
							| 
									
										
										
										
											2018-01-30 12:03:25 -07:00
										 |  |  |         dns.resolve4(ns.name, function (err, addresses) { | 
					
						
							|  |  |  |           console.log('ns addresses:'); | 
					
						
							|  |  |  |           console.log(addresses); | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |           if (err) { console.error(err); done(); return; } | 
					
						
							| 
									
										
										
										
											2018-01-30 12:03:25 -07:00
										 |  |  |           ns.type = 'A'; | 
					
						
							|  |  |  |           ns.address = addresses[0]; | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |           done(); | 
					
						
							| 
									
										
										
										
											2018-01-30 12:03:25 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // resolve addreses for all of the primary nameservers in parallel
 | 
					
						
							|  |  |  |       pNS.forEach(function (ns) { | 
					
						
							|  |  |  |         var status = { pending: true }; | 
					
						
							|  |  |  |         function done() { | 
					
						
							|  |  |  |           status.pending = false; | 
					
						
							|  |  |  |           // TODO: determine if the locally stored records should get updated
 | 
					
						
							|  |  |  |           var incomplete = tasks.filter(function (s) { return s.pending; }); | 
					
						
							|  |  |  |           if (incomplete.length < 1) { | 
					
						
							|  |  |  |             cb(null, pNS); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         getRecord(ns, done); | 
					
						
							|  |  |  |         return status; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-01-10 14:54:08 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |   engine.zones = { | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |     _immutableKeys: [ 'id', 'name', 'primary', 'serial', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ] | 
					
						
							|  |  |  |   , _mutableKeys: [ 'admin', 'expiration', 'minimum', 'refresh', 'retry', 'ttl', 'vanity' ] | 
					
						
							|  |  |  |   , _dateToSerial: function (date) { | 
					
						
							|  |  |  |       // conventionally the format is YYYYMMDDxx,
 | 
					
						
							|  |  |  |       // but since it's an integer and I don't want to keep track of incrementing xx,
 | 
					
						
							|  |  |  |       // epoch in seconds will do
 | 
					
						
							|  |  |  |       return parseInt(Math.round(date/1000).toString().slice(-10), 10); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |     // NOTE/TODO: despite the _, _toSoa is used outside this file (in lib/digd.js and lib/httpd.js)
 | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |   , _toSoa: function (domain) { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       var nameservers = domain.vanityNs || engine.primaryNameservers().map(function (n) { return n.name; }); | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       var index = Math.floor(Math.random() * nameservers.length) % nameservers.length; | 
					
						
							|  |  |  |       var nameserver = nameservers[index]; | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         id: domain.id | 
					
						
							|  |  |  |       , 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
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         // default is effectively admin@{domain name}
 | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |       , 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 || engine.zones._dateToSerial(domain.updatedAt || domain.createdAt || Date.now()) | 
					
						
							|  |  |  |       , sn: domain.serial || engine.zones._dateToSerial(domain.updatedAt || domain.createdAt || Date.now()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 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 // 4 weeks
 | 
					
						
							|  |  |  |       , ex: domain.expiration || 2419200 // 4 weeks
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // minimum -- how long to cache a non-existent domain (also the default ttl for BIND)
 | 
					
						
							|  |  |  |       , minimum: domain.minimum || 5 | 
					
						
							|  |  |  |       , nx: domain.minimum || 5 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |   , all: function (cb) { | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |       process.nextTick(function () { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         cb(null, db.zones().filter(notDeleted)); | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-17 17:01:44 -07:00
										 |  |  |   , get: function (queries, cb) { | 
					
						
							|  |  |  |       if (!Array.isArray(queries)) { | 
					
						
							|  |  |  |         queries = queries.names.map(function (n) { | 
					
						
							|  |  |  |           return { name: n }; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       var myDomains = db.zones().filter(function (d) { | 
					
						
							| 
									
										
										
										
											2018-01-17 17:01:44 -07:00
										 |  |  |         return queries.some(function (q) { | 
					
						
							| 
									
										
										
										
											2018-01-29 14:54:03 -07:00
										 |  |  |           return (d.name.toLowerCase() === q.name) && notDeleted(d); | 
					
						
							| 
									
										
										
										
											2018-01-17 17:01:44 -07:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |       process.nextTick(function () { | 
					
						
							|  |  |  |         cb(null, myDomains); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |   , touch: function (zone, cb) { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       db.zones.get(zone, function (err, existing) { | 
					
						
							|  |  |  |         if (err || !existing) { | 
					
						
							|  |  |  |           cb(err, null); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         existing.updatedAt = new Date().valueOf(); // toISOString();
 | 
					
						
							|  |  |  |         console.log('touch saving...'); | 
					
						
							|  |  |  |         db.zone.update(existing, function (err) { | 
					
						
							|  |  |  |           cb(err, !err && existing || null); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         return; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , save: function (zone, cb) { | 
					
						
							|  |  |  |       if (zone.id) { | 
					
						
							|  |  |  |         console.log('update zone!'); | 
					
						
							|  |  |  |         engine.zones.update(zone, cb); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         engine.zones.create(zone, cb); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , update: function (zone, cb) { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       db.zones.get({ id: zone.id }, function (err, found) { | 
					
						
							|  |  |  |         var dirty; | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         if (err) { | 
					
						
							|  |  |  |           console.log('error finding zone'); | 
					
						
							|  |  |  |           cb(new Error("Error finding zone for '" + zone.id + "'"), null); | 
					
						
							|  |  |  |           return; | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         if (!found) { | 
					
						
							|  |  |  |           console.log('no existing zone'); | 
					
						
							|  |  |  |           cb(new Error("zone for '" + zone.id + "' does not exist"), null); | 
					
						
							|  |  |  |           return; | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         console.log('found existing zone'); | 
					
						
							|  |  |  |         console.log(found); | 
					
						
							|  |  |  |         console.log(zone); | 
					
						
							|  |  |  |         Object.keys(zone).forEach(function (key) { | 
					
						
							|  |  |  |           if (-1 !== engine.zones._immutableKeys.indexOf(key)) { return; } | 
					
						
							|  |  |  |           if (found[key] !== zone[key]) { | 
					
						
							|  |  |  |             dirty = true; | 
					
						
							|  |  |  |             console.log('existing key', key, found[key], zone[key]); | 
					
						
							|  |  |  |             found[key] = zone[key]; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |         found.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
 | 
					
						
							|  |  |  |         if (dirty) { | 
					
						
							|  |  |  |           found.changedAt = found.updatedAt; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         console.log('saving...'); | 
					
						
							|  |  |  |         db.zones.update(found, function (err) { | 
					
						
							|  |  |  |           cb(err, !err && found || null); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |   , create: function (zone, cb) { | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |     var zoneName = (zone.name||'').toLowerCase(); | 
					
						
							|  |  |  |     db.zones.get({ name: zoneName }, function (err, found) { | 
					
						
							|  |  |  |       if (err) { | 
					
						
							|  |  |  |         console.error(err); | 
					
						
							|  |  |  |         cb(new Error("error attempting to create new zone '" + zoneName + "'")); | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       if (found) { | 
					
						
							|  |  |  |         cb(new Error("tried to create new zone, but '" + found.name + "' already exists")); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var newZone = { | 
					
						
							|  |  |  |         id: crypto.randomBytes(16).toString('hex'), | 
					
						
							|  |  |  |         name: zoneName | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       var nss = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |       newZone.createdAt = Date.now(); | 
					
						
							|  |  |  |       newZone.updatedAt = newZone.createdAt; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       /* | 
					
						
							|  |  |  |       Set only the mutable keys in the new zone from the proposed zone object | 
					
						
							|  |  |  |       */ | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |       Object.keys(zone).forEach(function (key) { | 
					
						
							|  |  |  |         //if (-1 !== engine.zones._immutableKeys.indexOf(key)) { return; }
 | 
					
						
							|  |  |  |         if (-1 === engine.zones._mutableKeys.indexOf(key)) { return; } | 
					
						
							|  |  |  |         newZone[key] = zone[key]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO create NS and A records for normal and vanity nameservers
 | 
					
						
							|  |  |  |       if (zone.vanity) { | 
					
						
							|  |  |  |         newZone.vanity = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         newZone.vanity = false; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // TODO: distinguish between primary and secondary zones
 | 
					
						
							|  |  |  |       // TODO: determine if we need to do anything special for delegation
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // create records for the primary nameservers (or vanity name servers)
 | 
					
						
							|  |  |  |       db.primaryNameservers.list().forEach(function (ns, i) { | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |         var nsx = 'ns' + (i + 1); | 
					
						
							|  |  |  |         var nsZone; | 
					
						
							|  |  |  |         var ttl = 43200; // 12h // TODO pick a well-reasoned number
 | 
					
						
							|  |  |  |         var now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (zone.vanity) { | 
					
						
							|  |  |  |           nsZone = nsx + '.' + newZone.name; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           nsZone = ns.name; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // NS example.com ns1.example.com 43200
 | 
					
						
							|  |  |  |         nss.push({ | 
					
						
							|  |  |  |           id: crypto.randomBytes(16).toString('hex') | 
					
						
							|  |  |  |         , createdAt: Date.now() | 
					
						
							|  |  |  |         , updatedAt: Date.now() | 
					
						
							|  |  |  |         , changedAt: Date.now() | 
					
						
							|  |  |  |         , zone: newZone.name | 
					
						
							|  |  |  |         , soa: true | 
					
						
							|  |  |  |         , type: 'NS' | 
					
						
							|  |  |  |         , data: nsZone | 
					
						
							|  |  |  |         , name: newZone.name | 
					
						
							|  |  |  |         , ttl: ttl | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         // A ns1.example.com 127.0.0.1 43200
 | 
					
						
							|  |  |  |         nss.push({ | 
					
						
							|  |  |  |           id: crypto.randomBytes(16).toString('hex') | 
					
						
							|  |  |  |         , createdAt: now | 
					
						
							|  |  |  |         , updatedAt: now | 
					
						
							|  |  |  |         , changedAt: now | 
					
						
							|  |  |  |         , zone: newZone.name | 
					
						
							|  |  |  |         , soa: true | 
					
						
							|  |  |  |         , type: ns.type | 
					
						
							|  |  |  |         , name: nsZone | 
					
						
							|  |  |  |         , address: ns.address | 
					
						
							|  |  |  |         , ttl: 43200 // 12h // TODO pick a good number
 | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 10:53:54 -07:00
										 |  |  |       db.zones.create(newZone, function (err) { | 
					
						
							|  |  |  |         // WIP: going to need to figure out how to manage this as a transaction
 | 
					
						
							|  |  |  |         // Significant benefit to having records owned by the zone is we won't have
 | 
					
						
							|  |  |  |         // records for zones that don't otherwise exist - at least at the engine level.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // every line below this one is not yet modified...
 | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |       nss.forEach(function (ns) { | 
					
						
							|  |  |  |         db.records.push(ns); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |       console.log('[zone] [create] saving...'); | 
					
						
							| 
									
										
										
										
											2018-01-30 22:03:05 -07:00
										 |  |  |       db.save(function (err) { | 
					
						
							|  |  |  |         cb(err, !err && newZone || null); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |   , destroy: function (zoneId, cb) { | 
					
						
							|  |  |  |       var zone; | 
					
						
							|  |  |  |       var records; | 
					
						
							| 
									
										
										
										
											2018-01-31 19:19:11 -07:00
										 |  |  |       var now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |       db.zones.filter(notDeleted).some(function (z) { | 
					
						
							|  |  |  |         if (zoneId === z.id) { | 
					
						
							|  |  |  |           zone = z; | 
					
						
							| 
									
										
										
										
											2018-01-31 19:19:11 -07:00
										 |  |  |           z.deletedAt = now; | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!zone) { | 
					
						
							|  |  |  |         process.nextTick(function () { | 
					
						
							|  |  |  |           cb(null, null); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       records = []; | 
					
						
							|  |  |  |       db.records.filter(notDeleted).forEach(function (r) { | 
					
						
							|  |  |  |         if (zone.name === r.zone) { | 
					
						
							| 
									
										
										
										
											2018-01-31 19:19:11 -07:00
										 |  |  |           r.deletedAt = now; | 
					
						
							| 
									
										
										
										
											2018-01-31 19:09:26 -07:00
										 |  |  |           records.push(r); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log('[zone] [destroy] saving...'); | 
					
						
							|  |  |  |       db.save(function (err) { | 
					
						
							|  |  |  |         zone.records = records; | 
					
						
							|  |  |  |         cb(err, !err && zone || null); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |   engine.records = { | 
					
						
							| 
									
										
										
										
											2018-01-10 01:25:56 -07:00
										 |  |  |     all: function (cb) { | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |       process.nextTick(function () { | 
					
						
							| 
									
										
										
										
											2018-01-29 14:54:03 -07:00
										 |  |  |         cb(null, db.records.slice(0).filter(notDeleted)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , one: function (id, cb) { | 
					
						
							|  |  |  |       var myRecord; | 
					
						
							|  |  |  |       db.records.slice(0).some(function (r) { | 
					
						
							|  |  |  |         if (id && id === r.id) { | 
					
						
							|  |  |  |           if (notDeleted(r)) { | 
					
						
							|  |  |  |             myRecord = r; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       process.nextTick(function () { | 
					
						
							|  |  |  |         cb(null, myRecord); | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , get: function (query, cb) { | 
					
						
							|  |  |  |       var myRecords = db.records.slice(0).filter(function (r) { | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |         if ('string' !== typeof r.name) { | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |         // 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) { | 
					
						
							| 
									
										
										
										
											2018-01-29 14:54:03 -07:00
										 |  |  |           if (notDeleted(r)) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-01-10 08:14:05 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       process.nextTick(function () { | 
					
						
							|  |  |  |         cb(null, myRecords); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |   , save: function (record, cb) { | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |       function touchZone(err, r) { | 
					
						
							|  |  |  |         if (err) { cb(err); } | 
					
						
							|  |  |  |         if (!r) { cb(null, null); } | 
					
						
							|  |  |  |         engine.zones.touch({ name: r.zone }, cb); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |       if (record.id) { | 
					
						
							|  |  |  |         console.log('update record!'); | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         engine.records.update(record, touchZone); | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         engine.records.create(record, touchZone); | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , update: function (record, cb) { | 
					
						
							|  |  |  |       var existing; | 
					
						
							|  |  |  |       var dirty; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       db.records.some(function (r) { | 
					
						
							|  |  |  |         if (r.id === record.id) { | 
					
						
							|  |  |  |           existing = r; | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!existing) { | 
					
						
							|  |  |  |         console.log('no existing record'); | 
					
						
							|  |  |  |         cb(new Error("record for '" + record.id + "' does not exist"), null); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log('found existing record'); | 
					
						
							|  |  |  |       console.log(existing); | 
					
						
							|  |  |  |       console.log(record); | 
					
						
							|  |  |  |       Object.keys(record).forEach(function (key) { | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |         var keys = [ 'name', 'id', 'zone', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ]; | 
					
						
							|  |  |  |         if (-1 !== keys.indexOf(key)) { return; } | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |         if (existing[key] !== record[key]) { | 
					
						
							|  |  |  |           dirty = true; | 
					
						
							|  |  |  |           console.log(existing[key], record[key]); | 
					
						
							|  |  |  |           existing[key] = record[key]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |       record.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
 | 
					
						
							| 
									
										
										
										
											2018-01-23 15:21:20 -07:00
										 |  |  |       if (dirty) { | 
					
						
							|  |  |  |         record.changedAt = record.updatedAt; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log('saving...'); | 
					
						
							|  |  |  |       db.save(function (err) { | 
					
						
							|  |  |  |         cb(err, !err && existing || null); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-26 01:42:24 -07:00
										 |  |  |   , create: function (record, cb) { | 
					
						
							|  |  |  |       var obj = { id: crypto.randomBytes(16).toString('hex') }; | 
					
						
							|  |  |  |       console.log('found existing record'); | 
					
						
							|  |  |  |       console.log(record); | 
					
						
							|  |  |  |       //var keys = [ 'name', 'id', 'zone', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ];
 | 
					
						
							|  |  |  |       //var okeys = [ 'name', 'zone', 'admin', 'data', 'expiration', 'minimum', 'serial', 'retry', 'refresh', 'ttl', 'type' ]; // primary
 | 
					
						
							|  |  |  |       var okeys = [ 'name', 'zone', 'type', 'data', 'class', 'ttl', 'address' | 
					
						
							|  |  |  |                   , 'exchange', 'priority', 'port', 'value', 'tag', 'flag', 'aname' ]; | 
					
						
							|  |  |  |       okeys.forEach(function (key) { | 
					
						
							|  |  |  |         if ('undefined' !== typeof record[key]) { | 
					
						
							|  |  |  |           obj[key] = record[key]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       record.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
 | 
					
						
							|  |  |  |       //record.changedAt = record.updatedAt;
 | 
					
						
							|  |  |  |       record.insertedAt = record.updatedAt; | 
					
						
							|  |  |  |       record.createdAt = record.updatedAt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log('saving new...'); | 
					
						
							|  |  |  |       db.records.push(record); | 
					
						
							|  |  |  |       db.save(function (err) { | 
					
						
							|  |  |  |         cb(err, record); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 14:54:03 -07:00
										 |  |  |   , destroy: function (id, cb) { | 
					
						
							|  |  |  |       var record; | 
					
						
							|  |  |  |       db.records.some(function (r/*, i*/) { | 
					
						
							|  |  |  |         if (id === r.id) { | 
					
						
							|  |  |  |           record = r; | 
					
						
							|  |  |  |           r.deletedAt = Date.now(); | 
					
						
							|  |  |  |           //record = db.records.splice(i, 1);
 | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       process.nextTick(function () { | 
					
						
							|  |  |  |         db.save(function (err) { | 
					
						
							|  |  |  |           cb(err, record); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-05 09:16:27 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return engine; | 
					
						
							|  |  |  | }; |