153 lines
4.1 KiB
JavaScript
153 lines
4.1 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.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 },
|
|
salt: { type: 'String', priv: true },
|
|
created_at: { type: 'String' },
|
|
modified_at: { type: 'String' }
|
|
});
|
|
|
|
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);
|
|
}
|
|
|