-
Notifications
You must be signed in to change notification settings - Fork 113
/
index.js
151 lines (132 loc) · 3.92 KB
/
index.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
'use strict';
const debug = require('debug')('koa-session');
const ContextSession = require('./lib/context');
const util = require('./lib/util');
const assert = require('assert');
const uuid = require('uuid');
const is = require('is-type-of');
const CONTEXT_SESSION = Symbol('context#contextSession');
const _CONTEXT_SESSION = Symbol('context#_contextSession');
/**
* Initialize session middleware with `opts`:
*
* - `key` session cookie name ["koa.sess"]
* - all other options are passed as cookie options
*
* @param {Object} [opts]
* @param {Application} app, koa application instance
* @api public
*/
module.exports = function(opts, app) {
// session(app[, opts])
if (opts && typeof opts.use === 'function') {
[ app, opts ] = [ opts, app ];
}
// app required
if (!app || typeof app.use !== 'function') {
throw new TypeError('app instance required: `session(opts, app)`');
}
opts = formatOpts(opts);
extendContext(app.context, opts);
return async function session(ctx, next) {
const sess = ctx[CONTEXT_SESSION];
if (sess.store) await sess.initFromExternal();
try {
await next();
} catch (err) {
throw err;
} finally {
if (opts.autoCommit) {
await sess.commit();
}
}
};
};
/**
* format and check session options
* @param {Object} opts session options
* @return {Object} new session options
*
* @api private
*/
function formatOpts(opts) {
opts = opts || {};
// key
opts.key = opts.key || 'koa.sess';
// back-compat maxage
if (!('maxAge' in opts)) opts.maxAge = opts.maxage;
// defaults
if (opts.overwrite == null) opts.overwrite = true;
if (opts.httpOnly == null) opts.httpOnly = true;
// delete null sameSite config
if (opts.sameSite == null) delete opts.sameSite;
if (opts.signed == null) opts.signed = true;
if (opts.autoCommit == null) opts.autoCommit = true;
debug('session options %j', opts);
// setup encoding/decoding
if (typeof opts.encode !== 'function') {
opts.encode = util.encode;
}
if (typeof opts.decode !== 'function') {
opts.decode = util.decode;
}
const store = opts.store;
if (store) {
assert(is.function(store.get), 'store.get must be function');
assert(is.function(store.set), 'store.set must be function');
assert(is.function(store.destroy), 'store.destroy must be function');
}
const externalKey = opts.externalKey;
if (externalKey) {
assert(is.function(externalKey.get), 'externalKey.get must be function');
assert(is.function(externalKey.set), 'externalKey.set must be function');
}
const ContextStore = opts.ContextStore;
if (ContextStore) {
assert(is.class(ContextStore), 'ContextStore must be a class');
assert(is.function(ContextStore.prototype.get), 'ContextStore.prototype.get must be function');
assert(is.function(ContextStore.prototype.set), 'ContextStore.prototype.set must be function');
assert(is.function(ContextStore.prototype.destroy), 'ContextStore.prototype.destroy must be function');
}
if (!opts.genid) {
if (opts.prefix) opts.genid = () => `${opts.prefix}${uuid.v4()}`;
else opts.genid = uuid.v4;
}
return opts;
}
/**
* extend context prototype, add session properties
*
* @param {Object} context koa's context prototype
* @param {Object} opts session options
*
* @api private
*/
function extendContext(context, opts) {
if (context.hasOwnProperty(CONTEXT_SESSION)) {
return;
}
Object.defineProperties(context, {
[CONTEXT_SESSION]: {
get() {
if (this[_CONTEXT_SESSION]) return this[_CONTEXT_SESSION];
this[_CONTEXT_SESSION] = new ContextSession(this, opts);
return this[_CONTEXT_SESSION];
},
},
session: {
get() {
return this[CONTEXT_SESSION].get();
},
set(val) {
this[CONTEXT_SESSION].set(val);
},
configurable: true,
},
sessionOptions: {
get() {
return this[CONTEXT_SESSION].opts;
},
},
});
}