UNPKG

better-sqlite3-session-store

Version:
231 lines (199 loc) 4.51 kB
// @format const noop = () => {}; // NOTE: 1d = 86400s = 86400000ms const oneDay = 86400000; // NOTE: In Milliseconds const clearExpiredInterval = 900000; const tableName = "sessions"; const schema = ` CREATE TABLE IF NOT EXISTS ${tableName} ( sid TEXT NOT NULL PRIMARY KEY, sess JSON NOT NULL, expire TEXT NOT NULL ) `; module.exports = ({ Store }) => { class SqliteStore extends Store { constructor(options = {}) { super(options); if (!options.client) { throw new Error("A client must be directly provided to SqliteStore"); } this.expired = { clear: (options.expired && options.expired.clear) || true, intervalMs: (options.expired && options.expired.intervalMs) || clearExpiredInterval, }; this.client = options.client; this.createDb(); if (this.expired.clear) { this.startInterval(); } } startInterval() { setInterval( this.clearExpiredSessions.bind(this), this.expired.intervalMs ); } clearExpiredSessions() { let res; try { this.client .prepare( ` DELETE FROM ${tableName} WHERE datetime('now') > datetime(expire) ` ) .run(); } catch (err) { console.error(err); } } createDb() { this.client.exec(schema); } set(sid, sess, cb = noop) { let age; // NOTE: Express's temporal unit of choice is milliseconds: // http://expressjs.com/en/resources/middleware/session.html#:~:text=cookie.maxAge if (sess.cookie && sess.cookie.maxAge) { age = sess.cookie.maxAge; } else { age = oneDay; } const now = new Date().getTime(); const expire = new Date(now + age).toISOString(); const entry = { sid, sess: JSON.stringify(sess), expire }; let res; try { res = this.client .prepare( ` INSERT OR REPLACE INTO ${tableName} VALUES ( @sid, @sess, @expire ) ` ) .run(entry); } catch (err) { cb(err); return; } cb(null, res); } get(sid, cb = noop) { let res; try { res = this.client .prepare( ` SELECT sess FROM ${tableName} WHERE sid = @sid AND datetime('now') < datetime(expire) ` ) .get({ sid }); } catch (err) { cb(err); return; } if (res && res.sess) { cb(null, JSON.parse(res.sess)); } else { cb(null, null); } } destroy(sid, cb = noop) { let res; try { res = this.client .prepare( ` DELETE FROM ${tableName} WHERE sid = ? ` ) .run(sid); } catch (err) { cb(err); return; } cb(null, res); } length(cb = noop) { let res; try { res = this.client .prepare( ` SELECT COUNT(*) as count FROM ${tableName} ` ) .get(); } catch (err) { cb(err); return; } cb(null, res.count); } clear(cb = noop) { let res; try { res = this.client.prepare(`DELETE FROM ${tableName}`).run(); } catch (err) { cb(err); return; } cb(null, res); } touch(sid, sess, cb = noop) { const entry = { sid }; if (sess && sess.cookie && sess.cookie.expires) { entry.expire = new Date(sess.cookie.expires).toISOString(); } else { const now = new Date().getTime(); entry.expire = new Date(now + oneDay).toISOString(); } let res; try { res = this.client .prepare( ` UPDATE ${tableName} SET expire = @expire WHERE sid = @sid AND datetime('now') < datetime(expire) ` ) .run(entry); } catch (err) { cb(err); return; } cb(null, res); } all(cb = noop) { let res; try { res = this.client .prepare( ` SELECT * FROM ${tableName} ` ) .all(); } catch (err) { cb(err); return; } cb(null, res); } } return SqliteStore; };