Last active
August 29, 2015 14:01
-
-
Save timmywil/b0ab17daa4f2d52e62af to your computer and use it in GitHub Desktop.
Connect-based postgres session storage for the latest pg module
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Connect-based Postgres session storage | |
* Based off https://github.com/brianc/postgres-session | |
*/ | |
'use strict'; | |
var assert = require('assert'); | |
var pg = require('pg'); | |
var Store = require('express-session').Store; | |
/** | |
* Postgres session storage | |
* @constructor PostgresStore | |
* @param {Object} options | |
* @param {string} options.url Database URL connection string | |
*/ | |
function PostgresStore(options) { | |
Store.call(this, options); | |
assert(options && options.url, 'A database URL must be specified'); | |
this.url = options.url; | |
} | |
PostgresStore.prototype = Object.create(Store.prototype); | |
/** | |
* Create a callback for queries | |
* @private | |
* @param {Function} done pg done function | |
* @param {Function} callback Our completion callback | |
*/ | |
function release(done, callback) { | |
return function(err, result) { | |
done(); | |
callback.call(this, err, result); | |
}; | |
} | |
/** | |
* Connect to the postgres store | |
* @param {string} query | |
* @param {Array} data | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.query = function connect(query, data, callback) { | |
pg.connect(this.url, function(err, client, done) { | |
callback = release(done, callback); | |
if (err) { | |
callback(err); | |
return; | |
} | |
client.query(query, data, callback); | |
}); | |
}; | |
/** | |
* Retrieve the number of sessions | |
* @member {Function} length | |
* @memberof PostgresStore | |
* @instance | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.length = function length(callback) { | |
this.query('select count(*) from sessions', [], callback); | |
}; | |
/** | |
* Clear the table of all sessions | |
* @member {Function} clear | |
* @memberof PostgresStore | |
* @instance | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.clear = function clear(callback) { | |
this.query('truncate table sessions', [], callback); | |
}; | |
/** | |
* Set session data | |
* @memberof PostgresStore | |
* @instance | |
* @param {string} hash Session identifier | |
* @param {string} data Session data | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.set = function set(hash, data, callback) { | |
this.query( | |
'select sessionstore($1, $2)', | |
[hash, JSON.stringify(data)], | |
callback | |
); | |
}; | |
/** | |
* Retrieve a session | |
* @memberof PostgresStore | |
* @instance | |
* @param {string} hash Session identifier | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.get = function get(hash, callback) { | |
this.query( | |
'select data from sessions where key = $1', | |
[hash], | |
function(err, result) { | |
if (err) { | |
callback(err); | |
return; | |
} | |
var row; | |
// Parse data to JSON | |
result = result && result.rows && (row = result.rows[0]) && row.data && JSON.parse(row.data); | |
callback(null, result || null); | |
} | |
); | |
}; | |
/** | |
* Destroy a session | |
* @memberof PostgresStore | |
* @instance | |
* @param {string} hash Session identifier | |
* @param {Function} callback | |
*/ | |
PostgresStore.prototype.destroy = function destroy(hash, callback) { | |
this.query( | |
'delete from sessions where key = $1', | |
[hash], | |
callback | |
); | |
}; | |
/* Exports | |
---------------------------------------------------------------------- */ | |
module.exports = PostgresStore; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var assert = require('assert'); | |
var PostgresStore = require('../lib/PostgresStore'); | |
var config = require('../tasks/env').app.middleware.session.module.arguments[1]; | |
var uuid = require('../public/js/models/uuid'); | |
var sha = uuid(); | |
describe('sessions', function() { | |
var store = new PostgresStore(config); | |
it('set a new session', function(done) { | |
store.set(sha, { sid: 'awesome' }, function(err, result) { | |
assert.equal(result.rowCount, 1); | |
done(); | |
}); | |
}); | |
it('gets the created session', function(done) { | |
store.get(sha, function(err, result) { | |
if (err) return done(err); | |
assert(result.sid); | |
done(); | |
}); | |
}); | |
it('does not find a session not present', function(done) { | |
store.get('1', function(err, result) { | |
assert.equal(result, null); | |
done(); | |
}); | |
}); | |
it('returns the number of sessions', function(done) { | |
store.length(function(err, result) { | |
if (err) return done(err); | |
assert(result.rows[0].count > 0); | |
done(); | |
}); | |
}); | |
it('deletes the new session', function(done) { | |
store.destroy(sha, function(err) { | |
if (err) return done(err); | |
store.get(sha, function(err, result) { | |
assert.equal(result, null, 'Session destroyed'); | |
done(); | |
}); | |
}); | |
}); | |
it('clears all sessions', function(done) { | |
store.clear(function(err, result) { | |
if (err) return done(err); | |
assert.equal(result.rows.length, 0); | |
done(); | |
}); | |
}); | |
}); | |
describe('sessions - fail', function() { | |
// Unable to connect | |
var store = new PostgresStore({ url: config.url.replace('quickcue', 'quick') }); | |
it('does not connect to set a new session', function(done) { | |
store.set(sha, { sid: 'awesome' }, function(err) { | |
assert(err); | |
done(); | |
}); | |
}); | |
it('does not connect to get the created session', function(done) { | |
store.get(sha, function(err) { | |
assert(err); | |
done(); | |
}); | |
}); | |
it('does not connect to return the number of sessions', function(done) { | |
store.length(function(err) { | |
assert(err); | |
done(); | |
}); | |
}); | |
it('does not connect to delete the new session', function(done) { | |
store.destroy(sha, function(err) { | |
assert(err); | |
done(); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment