| 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
127
128
129
130
131
132
133
134 | 1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
| const d = require("describe-property");
const redis = require("redis");
const makeToken = require("../../utils/makeToken");
const Promise = require("../../utils/Promise");
const parseURL = require("../../utils/parseURL");
function sendCommand(client, command, args) {
args = args || [];
return new Promise(function (resolve, reject) {
client.send_command(command, args, function (error, value) {
if (error) {
reject(error);
} else {
resolve(value);
}
});
});
}
function makeUniqueKey(client, keyLength) {
const key = makeToken(keyLength);
// Try to set an empty string to reserve the key.
return sendCommand(client, "setnx", [key, ""]).then(function (result) {
if (result === 1) {
return key; // The key was available.
}
return makeUniqueKey(client, keyLength); // Try again.
});
}
/**
* Server-side storage for sessions using Redis.
*
* Options may be any of the following:
*
* - url The URL of the Redis instance
* - keyLength The length (in bytes) that will be used for unique
* cache keys. Defaults to 32
* - expireAfter The number of seconds after which sessions expire.
* Defaults to 0 (no expiration)
*
* Additionally, all options are passed through to `redis.createClient`.
*
* Example:
*
* let RedisStore = require('mach/middleware/session/RedisStore');
*
* app.use(mach.session, {
* store: new RedisStore({ url: 'redis://127.0.0.1:6379' })
* });
*
* Note: This store always checks for availability of keys in Redis before
* using them, so it should be safe to use alongside other programs that are
* using the same database. However, if you purge the store of all keys it will
* issue a FLUSHDB command to the database, so be careful. This operation never
* happens automatically.
*/
function RedisStore(options) {
options = options || {};
this.options = options;
this.keyLength = options.keyLength || 32;
this.ttl = options.expireAfter
? 1000 * options.expireAfter // expireAfter is given in seconds
: 0;
}
Object.defineProperties(RedisStore.prototype, {
getClient: d(function () {
if (!this._client) {
const options = this.options;
let hostname, port;
if (typeof options.url === "string") {
const parsedURL = parseURL(options.url);
hostname = parsedURL.hostname;
port = parsedURL.port;
}
this._client = redis.createClient(port, hostname, options);
}
return this._client;
}),
load: d(function (value) {
return sendCommand(this.getClient(), "get", [value]).then(function (json) {
return json ? JSON.parse(json) : {};
});
}),
save: d(function (session) {
const client = this.getClient();
const keyLength = this.keyLength;
const ttl = this.ttl;
return Promise.resolve(session._id || makeUniqueKey(client, keyLength)).then(function (key) {
session._id = key;
const json = JSON.stringify(session);
let promise;
if (ttl) {
promise = sendCommand(client, "psetex", [key, ttl, json]);
} else {
promise = sendCommand(client, "set", [key, json]);
}
return promise.then(function () {
return key;
});
});
}),
purge: d(function (key) {
if (key) {
return sendCommand(this.getClient(), "del", [key]);
}
return sendCommand(this.getClient(), "flushdb");
}),
destroy: d(function () {
return sendCommand(this.getClient(), "quit");
})
});
module.exports = RedisStore;
|