105 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			2.7 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 config = {
 | |
|     save: function (changes) {
 | |
|       deps.messenger.send({
 | |
|         type: 'com.daplie.goldilocks/config'
 | |
|       , changes: changes
 | |
|       });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   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
 | |
|   , mdnsId: mdnsId
 | |
|   };
 | |
| };
 |