'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/v4');
|
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;
|
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()}`;
|
else opts.genid = uuid;
|
}
|
|
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;
|
},
|
},
|
});
|
}
|