-
Notifications
You must be signed in to change notification settings - Fork 0
/
Store.js
126 lines (105 loc) · 3.18 KB
/
Store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Ironboy 2020
// An express-session store plugin
// for the best-sqlite3 database driver
// The express session documentation says that a store should
// extend Node EventEmitter, but actually it shold extend their
// basic Store class!
let esStore = Object;
try {
esStore = require("express-session").Store;
}
catch (e) { }
module.exports = class Store extends esStore {
constructor(settings) {
super();
this.settings = Object.assign({
// Defaults
// A bestSqlite db connection, need to be provided
db: null,
// A table that will be created and used to store sessions in
tableName: 'sessions',
// After how many minutes of inactivity a session is deleted
// 0 = never delete
deleteAfterInactivityMinutes: 120,
}, settings);
if (!this.settings.db || !this.settings.db.constructor.name === 'BestSqlite') {
throw (new Error('Please provide a bestSqlite db connection!'));
}
this.db = this.settings.db;
this.db.run(`
CREATE TABLE IF NOT EXISTS ${this.settings.tableName} (
sid TEXT PRIMARY KEY NOT NULL UNIQUE,
session TEXT,
lastupdate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`)
setInterval(() => this.deleteAfterInactivity(), 10000);
}
// Most of the methods a express
// session store needs are documented here
// https://www.npmjs.com/package/express-session#session-store-implementation
// cb = callback
// express sesion expects the store methods to reply
// by calling a callback function
all(cb = x => x) {
cb(null, this.db.run(`
SELECT session FROM ${this.settings.tableName}
`).map(x => x.session));
}
destroy(sid, cb = x => x) {
this.db.run(`
DELETE FROM ${this.settings.tableName}
WHERE sid = $sid
`, { sid });
cb(null);
}
clear(cb = x => x) {
this.db.run(
`DELETE FROM ${this.settings.tableName}`
);
cb(null);
}
length(cb = x => x) {
cb(null, this.db.run(`
SELECT COUNT(*) as len
FROM ${this.settings.tableName}`)[0].len
);
}
get(sid, cb = x => x) {
let x = this.db.run(`
SELECT session FROM ${this.settings.tableName}
WHERE sid = $sid
`, { sid });
x = x.length ? x[0].session : null;
cb(null, JSON.parse(x));
}
set(sid, session, cb = x => x) {
session = JSON.stringify(session);
this.destroy(sid);
this.db.run(`
INSERT INTO ${this.settings.tableName} (sid, session)
VALUES ($sid, $session)
`, { sid, session });
cb(null);
}
touch(...args) {
this.set(...args);
}
// Not documented: on is called on two eventhandlers
// 'connect' and 'disconnect'
// so handle this by calling the callback if the event is connect
// - since we are connected right away we will callback at once
// - and we do not seem to need to handle disconnect...
on(event, cb) {
event === 'connect' && cb();
}
// Delete sessions after inactivity
deleteAfterInactivity() {
let m = this.settings.deleteAfterInactivityMinutes;
if (!m) { return; }
this.db.run(`
DELETE FROM ${this.settings.tableName}
WHERE (strftime('%s','now') - strftime('%s', lastupdate)) / 60 > $m
`, { m });
}
}