Files
cloudron-box/auth/database.js
T
2014-04-06 21:35:37 -07:00

174 lines
4.6 KiB
JavaScript

'use strict';
var fs = require('fs'),
util = require('util'),
safe = require('safetydance'),
path = require('path'),
mkdirp = require('mkdirp');
var rootDir = '';
exports = module.exports = {
// tables
USERS_TABLE: null,
TOKENS_TABLE: null,
initialize: initialize,
firstTime: firstTime,
DatabaseError: DatabaseError,
Table: Table
};
// http://dustinsenos.com/articles/customErrorsInNode
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
function DatabaseError(err, reason) {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = safe.JSON.stringify(err);
this.code = err ? err.code : null;
this.reason = reason || DatabaseError.SERVER_ERROR;
this.statusCode = 500; // any db error is a server error
}
util.inherits(DatabaseError, Error);
DatabaseError.SERVER_ERROR = 1;
DatabaseError.INTERNAL_ERROR = 2;
DatabaseError.ALREADY_EXISTS = 3;
DatabaseError.NOT_FOUND = 4;
DatabaseError.RECORD_SCHEMA = 5;
function Table(dbFile, schema) {
this.dbFile = dbFile;
this.schema = schema;
for (var p in schema) {
if (schema[p].hashKey === true) this.hashKey = p;
else if (schema[p].rangeKey === true) this.rangeKey = p;
}
if (!('hashKey' in this)) throw(new Error('Table does not define a primary key'));
var data = safe.fs.readFileSync(this.dbFile);
this.cache = safe.JSON.parse(data) || { };
}
Table.prototype.put = function (obj, callback) {
var key = obj[this.hashKey];
if (!key) return callback(new DatabaseError('no hash key found', DatabaseError.RECORD_SCHEMA));
if (key in this.cache) return callback(new DatabaseError(null, DatabaseError.ALREADY_EXISTS));
this.cache[key] = obj;
fs.writeFileSync(this.dbFile, safe.JSON.stringify(this.cache, null, 4));
callback();
};
Table.prototype.update = function (obj, callback) {
var key = obj[this.hashKey];
if (!key) return callback(new DatabaseError('no hash key found', DatabaseError.RECORD_SCHEMA));
if (!(key in this.cache)) return callback(new DatabaseError(null, DatabaseError.NOT_FOUND));
this.cache[key] = obj;
fs.writeFileSync(this.dbFile, safe.JSON.stringify(this.cache, null, 4));
callback();
};
Table.prototype.get = function (key, callback) {
if (key in this.cache) return callback(null, this.cache[key]);
return callback(new DatabaseError(null, DatabaseError.NOT_FOUND));
};
Table.prototype.count = function () {
var i = 0;
for (var e in this.cache) {
if (this.cache.hasOwnProperty(e)) {
++i;
}
}
return i;
};
Table.prototype.remove = function (key, callback) {
var value = this.cache[key];
if (value) {
delete this.cache[key];
fs.writeFileSync(this.dbFile, safe.JSON.stringify(this.cache, null, 4));
return callback(null, value);
}
return callback(new DatabaseError(null, DatabaseError.NOT_FOUND));
};
// testing
Table.prototype.removeAll = function (callback) {
this.cache = { };
fs.writeFileSync(this.dbFile, '');
return callback(null);
};
Table.prototype.getAll = function (privates, callback) {
var result = [];
for (var item in this.cache) {
if (this.cache.hasOwnProperty(item)) {
// TODO make deep copies?
if (!privates) {
result.push(this.removePrivates(this.cache[item]));
} else {
result.push(this.cache[item]);
}
}
}
return callback(null, result);
};
Table.prototype.removePrivates = function (obj) {
var res = { };
for (var p in this.schema) {
if (this.schema[p].priv || !(p in obj))
continue;
res[p] = obj[p]; // ## make deep copy?
}
return res;
};
function initialize(config) {
rootDir = path.join(config.configRoot, 'db');
mkdirp.sync(rootDir);
exports.USERS_TABLE = new Table(rootDir + '/users', {
username: { type: 'String', hashKey: true },
email: { type: 'String' },
password: { type: 'String', priv: true },
publicPem: { type: 'String' },
privatePemCipher: { type: 'String', priv: true },
salt: { type: 'String', priv: true },
created_at: { type: 'String' },
modified_at: { type: 'String' },
admin: { type: 'Boolean' }
});
exports.TOKENS_TABLE = new Table(rootDir + '/tokens', {
token: { type: 'String', hashKey: true },
username: { type: 'String', priv: true },
email: { type: 'String', priv: true },
expires: { type: 'String' }
});
return true;
}
function firstTime() {
return (exports.USERS_TABLE.count() === 0);
}