forked from coolaj86/goldilocks.js
125 lines
3.2 KiB
JavaScript
125 lines
3.2 KiB
JavaScript
'use strict';
|
|
|
|
var PromiseA = require('bluebird');
|
|
var path = require('path');
|
|
var fs = PromiseA.promisifyAll(require('fs'));
|
|
|
|
module.exports.create = function (deps, conf) {
|
|
var hrIds = require('human-readable-ids').humanReadableIds;
|
|
var scmp = require('scmp');
|
|
var storageDir = path.join(__dirname, '..', 'var');
|
|
|
|
function read(fileName) {
|
|
return fs.readFileAsync(path.join(storageDir, fileName))
|
|
.then(JSON.parse, function (err) {
|
|
if (err.code === 'ENOENT') {
|
|
return {};
|
|
}
|
|
throw err;
|
|
});
|
|
}
|
|
function write(fileName, obj) {
|
|
return fs.mkdirAsync(storageDir).catch(function (err) {
|
|
if (err.code !== 'EEXIST') {
|
|
console.error('failed to mkdir', storageDir, err.toString());
|
|
}
|
|
}).then(function () {
|
|
return fs.writeFileAsync(path.join(storageDir, fileName), JSON.stringify(obj), 'utf8');
|
|
});
|
|
}
|
|
|
|
var owners = {
|
|
_filename: 'owners.json'
|
|
, all: function () {
|
|
return read(this._filename).then(function (owners) {
|
|
return Object.keys(owners).map(function (id) {
|
|
var owner = owners[id];
|
|
owner.id = id;
|
|
return owner;
|
|
});
|
|
});
|
|
}
|
|
, get: function (id) {
|
|
// While we could directly read the owners file and access the id directly from
|
|
// the resulting object I'm not sure of the details of how the object key lookup
|
|
// works or whether that would expose us to timing attacks.
|
|
// See https://codahale.com/a-lesson-in-timing-attacks/
|
|
return this.all().then(function (owners) {
|
|
return owners.filter(function (owner) {
|
|
return scmp(id, owner.id);
|
|
})[0];
|
|
});
|
|
}
|
|
, exists: function (id) {
|
|
return this.get(id).then(function (owner) {
|
|
return !!owner;
|
|
});
|
|
}
|
|
, set: function (id, obj) {
|
|
var self = this;
|
|
return read(self._filename).then(function (owners) {
|
|
obj.id = id;
|
|
owners[id] = obj;
|
|
return write(self._filename, owners);
|
|
});
|
|
}
|
|
};
|
|
|
|
var confCb;
|
|
var config = {
|
|
save: function (changes) {
|
|
deps.messenger.send({
|
|
type: 'com.daplie.goldilocks/config'
|
|
, changes: changes
|
|
});
|
|
|
|
return new deps.PromiseA(function (resolve, reject) {
|
|
var timeoutId = setTimeout(function () {
|
|
reject(new Error('Did not receive config update from main process in a reasonable time'));
|
|
confCb = null;
|
|
}, 15*1000);
|
|
|
|
confCb = function (config) {
|
|
confCb = null;
|
|
clearTimeout(timeoutId);
|
|
resolve(config);
|
|
};
|
|
});
|
|
}
|
|
};
|
|
function updateConf(config) {
|
|
if (confCb) {
|
|
confCb(config);
|
|
}
|
|
}
|
|
|
|
var mdnsId = {
|
|
_filename: 'mdns-id'
|
|
, get: function () {
|
|
var self = this;
|
|
return read("mdns-id").then(function (result) {
|
|
if (typeof result !== 'string') {
|
|
throw new Error('mDNS ID not present');
|
|
}
|
|
return result;
|
|
}).catch(function () {
|
|
return self.set(hrIds.random());
|
|
});
|
|
}
|
|
|
|
, set: function (value) {
|
|
var self = this;
|
|
return write(self._filename, value).then(function () {
|
|
return self.get();
|
|
});
|
|
}
|
|
};
|
|
|
|
return {
|
|
owners: owners
|
|
, config: config
|
|
, updateConf: updateConf
|
|
, mdnsId: mdnsId
|
|
};
|
|
};
|