diff --git a/lib/httpd.js b/lib/httpd.js index 11a05cb..3ff58cd 100644 --- a/lib/httpd.js +++ b/lib/httpd.js @@ -100,7 +100,8 @@ module.exports.create = function (cli, engine/*, dnsd*/) { })) { next(); } else { - next(new Error("no claim to '" + claim + "' in token")); + res.send({ error: { message: "no claim to '" + claim + "' in token" } }); + return; } }; } @@ -212,8 +213,12 @@ module.exports.create = function (cli, engine/*, dnsd*/) { } app.get('/api/zones/:zone/records', function (req, res) { var zonename = req.params.zone; + // [{ name: zonename }] engine.zones.get({ names: [ zonename ] }, function (err, zones) { + console.log('zone:'); + console.log(zones[0]); var zone = engine.zoneToSoa(zones[0]); + console.log(zone); zone.class = zone.className; zone.type = zone.typeName; engine.records.all(function (err, records) { @@ -251,7 +256,7 @@ module.exports.create = function (cli, engine/*, dnsd*/) { if ('SOA' === record.type) { // TODO be strict about what can be edited - engine.records.save(record, function (err, record) { + engine.zones.save(record, function (err, record) { res.send({ success: true }); }); } else { diff --git a/lib/public/index.html b/lib/public/index.html index 3b635ba..c039c9d 100644 --- a/lib/public/index.html +++ b/lib/public/index.html @@ -117,30 +117,30 @@
SOA - .example.com + - + @@ -150,8 +150,8 @@ A / AAAA .example.com - - + +
diff --git a/lib/public/js/app.js b/lib/public/js/app.js index 5d24b4d..e78bb53 100644 --- a/lib/public/js/app.js +++ b/lib/public/js/app.js @@ -2,6 +2,8 @@ 'use strict'; var cache = { recordsMap: {} }; + var myZone; + window.ADNS = { cache: cache, $qs: $qs }; if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector; @@ -152,8 +154,7 @@ console.log(tpls.recordsMap); cache.records.forEach(function (record) { el = document.createElement('div'); - console.log('record.type:'); - console.log(record.type); + console.log('record.type:', record.type); el.innerHTML = tpls.recordsMap[record.type.toLowerCase()]; console.log(el); console.log($qs('.js-record-name', el)); @@ -220,62 +221,74 @@ $on('button.js-zone-name', 'click', function (ev) { var zone = ev.target.dataset.name; + myZone = zone; return fetchRecords(zone);/*.then(function () { });*/ }); $on('select.js-record-form-type', 'change', function (ev) { var type = ev.target.value; + var $tpl; console.log("form type:", type); if (!tpls.formsMap) { tpls.formsMap = {}; - tpls.formsMap.soa = $qs('.js-record-form-soa').outerHTML; - tpls.formsMap.ns = $qs('.js-record-form-ns').outerHTML; - tpls.formsMap.a = $qs('.js-record-form-a').outerHTML; - tpls.formsMap.aaaa = $qs('.js-record-form-aaaa').outerHTML; - tpls.formsMap.aname = $qs('.js-record-form-aname').outerHTML; - tpls.formsMap.caa = $qs('.js-record-form-caa').outerHTML; - tpls.formsMap.cname = $qs('.js-record-form-cname').outerHTML; - tpls.formsMap.mx = $qs('.js-record-form-mx').outerHTML; - tpls.formsMap.ptr = $qs('.js-record-form-ptr').outerHTML; - tpls.formsMap.srv = $qs('.js-record-form-srv').outerHTML; - tpls.formsMap.txt = $qs('.js-record-form-txt').outerHTML; - tpls.formsMap.typex = $qs('.js-record-form-typex').outerHTML; + tpls.formsMap.SOA = $qs('.js-record-form-soa').outerHTML; + tpls.formsMap.NS = $qs('.js-record-form-ns').outerHTML; + tpls.formsMap.A = $qs('.js-record-form-a').outerHTML; + tpls.formsMap.AAAA = $qs('.js-record-form-aaaa').outerHTML; + tpls.formsMap.ANAME = $qs('.js-record-form-aname').outerHTML; + tpls.formsMap.CAA = $qs('.js-record-form-caa').outerHTML; + tpls.formsMap.CNAME = $qs('.js-record-form-cname').outerHTML; + tpls.formsMap.MX = $qs('.js-record-form-mx').outerHTML; + tpls.formsMap.PTR = $qs('.js-record-form-ptr').outerHTML; + tpls.formsMap.SRV = $qs('.js-record-form-srv').outerHTML; + tpls.formsMap.TXT = $qs('.js-record-form-txt').outerHTML; + tpls.formsMap.typeX = $qs('.js-record-form-typex').outerHTML; } - $qs('.js-record-form-tpl').innerHTML = tpls.formsMap[type] || ''; + $tpl = $qs('.js-record-form-tpl'); + $tpl.innerHTML = tpls.formsMap[type] || ''; + $qs('.js-record-type', $tpl).innerText = type; + $qs('.js-record-zone', $tpl).innerText = myZone; }); $on('button.js-record-edit', 'click', function (ev) { var id = ev.target.parentElement.querySelector('.js-record-id').value; var record = cache.recordsMap[id]; - var formTpl = tpls.formsMap[record.type.toLowerCase()]; + var formTpl = tpls.formsMap[record.type]; + var $tpl; if (!formTpl) { - formTpl = tpls.formsMap.typex; + formTpl = tpls.formsMap.typeX; } console.log(ev.target); console.log(id); console.log(record); - formTpl = tpls.formsMap[(record.type||'typex').toLowerCase()]; - $qs('select.js-record-form-type').value = (record.type||'typex').toLowerCase(); + formTpl = tpls.formsMap[(record.type||'typeX')]; + $qs('select.js-record-form-type').value = (record.type||'typeX'); $qs('select.js-record-form-type').dispatchEvent(new Event('change', { bubbles: true })); - $qs('.js-record-form-tpl').innerHTML = formTpl || ''; + $tpl = $qs('.js-record-form-tpl'); + $tpl.innerHTML = formTpl || ''; record.host = record.name.replace(new RegExp('\\.?' + (record.zone || record.name).replace(/\./g, '\\.') + '$'), ''); console.log('record.type:'); - console.log(record.type.toLowerCase()); + console.log(record.type); Object.keys(record).forEach(function (key) { - var $el = $qs('.js-record-' + key, $qs('.js-record-form-tpl')); + var $el = $qs('.js-record-' + key, $tpl); if (!$el) { return; } $el.value = record[key]; }); - if (!record.host) { $qs('.js-record-host').placeholder = '@'; } - $qs('.js-record-type', $qs('.js-record-form-tpl')).innerHTML = record.type; + if ('SOA' === record.type) { + $qs('.js-record-name').disabled = 'disabled'; + } else { + if (!record.host) { $qs('.js-record-host', $tpl).placeholder = '@'; } + } + $qs('.js-record-type', $tpl).innerHTML = record.type; + $qs('.js-record-zone', $tpl).innerText = myZone; }); $on('button.js-record-save', 'click', function (ev) { @@ -302,6 +315,16 @@ if (!record.id) { record.id = ''; } + if (!record.zone) { + record.zone = myZone; + } + if (!record.name) { + if (record.host) { + record.name = record.host + '.' + myZone; + } else { + record.name = myZone; + } + } record.type = record.type || $qs('.js-record-type', $pel).innerHTML || $qs('.js-record-type', $pel).value; console.log('record.type:', record.type); @@ -318,7 +341,8 @@ } else { console.log('keys:', Object.keys(record)); Object.keys(record).forEach(function (key) { - console.log(key); + console.log('key:', key); + //if ('SOA' === record.type && 'zone' === key) { return; } if (existingRecord[key].toString() !== record[key].toString()) { change = true; existingRecord[key] = record[key]; @@ -340,6 +364,11 @@ } ).then(function (resp) { return resp.json().then(function (data) { + if (data.error) { + console.error(data); + window.alert(data.error.message); + return; + } console.log('result:', data); }); }); diff --git a/lib/store.json.js b/lib/store.json.js index 6f5e03d..bf421e7 100644 --- a/lib/store.json.js +++ b/lib/store.json.js @@ -5,6 +5,7 @@ module.exports.create = function (opts) { var engine = { db: null }; var db = require(opts.filepath); + var stat = require('fs').statSync(opts.filepath); var crypto = require('crypto'); db.primaryNameservers.forEach(function (ns) { if (!ns.id) { @@ -19,6 +20,8 @@ module.exports.create = function (opts) { if (!zone.id) { zone.id = crypto.randomBytes(16).toString('hex'); } + if (!zone.createdAt) { zone.createdAt = stat.mtime.valueOf(); } + if (!zone.updatedAt) { zone.updatedAt = stat.mtime.valueOf(); } }); db.records.forEach(function (record) { if (!record.id) { @@ -57,7 +60,8 @@ module.exports.create = function (opts) { var index = Math.floor(Math.random() * nameservers.length) % nameservers.length; var nameserver = nameservers[index]; return { - name: domain.name + id: domain.id + , name: domain.name , typeName: 'SOA' , className: 'IN' , ttl: domain.ttl || 60 @@ -119,6 +123,70 @@ module.exports.create = function (opts) { cb(null, myDomains); }); } + , touch: function (zone, cb) { + var existing; + db.zones.some(function (z) { + if (z.id && zone.id === z.id) { existing = z; return true; } + if (z.name && zone.name === z.name) { existing = z; return true; } + }); + if (!existing) { + cb(null, null); + return; + } + existing.updatedAt = new Date().valueOf(); // toISOString(); + console.log('touch saving...'); + db.save(function (err) { + cb(err, !err && existing || null); + }); + } + , 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) { + var existing; + var dirty; + + db.zones.some(function (z) { + if (z.id === zone.id) { + existing = z; + return true; + } + }); + + if (!existing) { + console.log('no existing zone'); + cb(new Error("zone for '" + zone.id + "' does not exist"), null); + return; + } + + console.log('found existing zone'); + console.log(existing); + console.log(zone); + Object.keys(zone).forEach(function (key) { + var keys = [ 'name', 'id', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ]; + if (-1 !== keys.indexOf(key)) { return; } + if (existing[key] !== zone[key]) { + dirty = true; + console.log('existing key', key, existing[key], zone[key]); + existing[key] = zone[key]; + } + }); + + zone.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000); + if (dirty) { + zone.changedAt = zone.updatedAt; + } + + console.log('saving...'); + db.save(function (err) { + cb(err, !err && existing || null); + }); + } }; engine.records = { all: function (cb) { @@ -144,11 +212,17 @@ module.exports.create = function (opts) { }); } , save: function (record, cb) { + function touchZone(err, r) { + if (err) { cb(err); } + if (!r) { cb(null, null); } + engine.zones.touch({ name: r.zone }, cb); + } + if (record.id) { console.log('update record!'); - engine.records.update(record, cb); + engine.records.update(record, touchZone); } else { - engine.records.create(record, cb); + engine.records.create(record, touchZone); } } , update: function (record, cb) { @@ -172,6 +246,8 @@ module.exports.create = function (opts) { console.log(existing); console.log(record); Object.keys(record).forEach(function (key) { + var keys = [ 'name', 'id', 'zone', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ]; + if (-1 !== keys.indexOf(key)) { return; } if (existing[key] !== record[key]) { dirty = true; console.log(existing[key], record[key]); @@ -179,7 +255,7 @@ module.exports.create = function (opts) { } }); - record.updatedAt = new Date().toISOString(); // Math.round(Date.now() / 1000); + record.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000); if (dirty) { record.changedAt = record.updatedAt; } @@ -189,6 +265,31 @@ module.exports.create = function (opts) { cb(err, !err && existing || null); }); } + , 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); + }); + } }; return engine;