node_modules updated, buefy add to lib folder
This commit is contained in:
+323
@@ -0,0 +1,323 @@
|
||||
'use strict'
|
||||
|
||||
const MongoClient = require('mongodb')
|
||||
|
||||
function withCallback(promise, cb) {
|
||||
// Assume that cb is a function - type checks and handling type errors
|
||||
// can be done by caller
|
||||
if (cb) {
|
||||
promise
|
||||
.then(res => cb(null, res))
|
||||
.catch(cb)
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
||||
function defaultSerializeFunction(session) {
|
||||
// Copy each property of the session to a new object
|
||||
const obj = {}
|
||||
let prop
|
||||
|
||||
for (prop in session) {
|
||||
if (prop === 'cookie') {
|
||||
// Convert the cookie instance to an object, if possible
|
||||
// This gets rid of the duplicate object under session.cookie.data property
|
||||
obj.cookie = session.cookie.toJSON ? session.cookie.toJSON() : session.cookie
|
||||
} else {
|
||||
obj[prop] = session[prop]
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
function computeTransformFunctions(options, defaultStringify) {
|
||||
if (options.serialize || options.unserialize) {
|
||||
return {
|
||||
serialize: options.serialize || defaultSerializeFunction,
|
||||
unserialize: options.unserialize || (x => x)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.stringify === false || defaultStringify === false) {
|
||||
return {
|
||||
serialize: defaultSerializeFunction,
|
||||
unserialize: x => x
|
||||
}
|
||||
}
|
||||
|
||||
if (options.stringify === true || defaultStringify === true) {
|
||||
return {
|
||||
serialize: JSON.stringify,
|
||||
unserialize: JSON.parse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (connect) {
|
||||
const Store = connect.Store || connect.session.Store
|
||||
const MemoryStore = connect.MemoryStore || connect.session.MemoryStore
|
||||
|
||||
class MongoStore extends Store {
|
||||
constructor(options) {
|
||||
options = options || {}
|
||||
|
||||
/* Fallback */
|
||||
if (options.fallbackMemory && MemoryStore) {
|
||||
return new MemoryStore()
|
||||
}
|
||||
|
||||
super(options)
|
||||
|
||||
/* Options */
|
||||
this.ttl = options.ttl || 1209600 // 14 days
|
||||
this.collectionName = options.collection || 'sessions'
|
||||
this.autoRemove = options.autoRemove || 'native'
|
||||
this.autoRemoveInterval = options.autoRemoveInterval || 10
|
||||
this.transformFunctions = computeTransformFunctions(options, true)
|
||||
|
||||
this.options = options
|
||||
|
||||
this.changeState('init')
|
||||
|
||||
const newConnectionCallback = (err, db) => {
|
||||
if (err) {
|
||||
this.connectionFailed(err)
|
||||
} else {
|
||||
this.handleNewConnectionAsync(db)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.url) {
|
||||
// New native connection using url + mongoOptions
|
||||
MongoClient.connect(options.url, options.mongoOptions || {}, newConnectionCallback)
|
||||
} else if (options.mongooseConnection) {
|
||||
// Re-use existing or upcoming mongoose connection
|
||||
if (options.mongooseConnection.readyState === 1) {
|
||||
this.handleNewConnectionAsync(options.mongooseConnection.db)
|
||||
} else {
|
||||
options.mongooseConnection.once('open', () => this.handleNewConnectionAsync(options.mongooseConnection.db))
|
||||
}
|
||||
} else if (options.db && options.db.listCollections) {
|
||||
// Re-use existing or upcoming native connection
|
||||
if (options.db.openCalled || options.db.openCalled === undefined) { // OpenCalled is undefined in mongodb@2.x
|
||||
this.handleNewConnectionAsync(options.db)
|
||||
} else {
|
||||
options.db.open(newConnectionCallback)
|
||||
}
|
||||
} else if (options.dbPromise) {
|
||||
options.dbPromise
|
||||
.then(db => this.handleNewConnectionAsync(db))
|
||||
.catch(err => this.connectionFailed(err))
|
||||
} else {
|
||||
throw new Error('Connection strategy not found')
|
||||
}
|
||||
|
||||
this.changeState('connecting')
|
||||
}
|
||||
|
||||
connectionFailed(err) {
|
||||
this.changeState('disconnected')
|
||||
throw err
|
||||
}
|
||||
|
||||
handleNewConnectionAsync(db) {
|
||||
this.db = db
|
||||
return this
|
||||
.setCollection(db.collection(this.collectionName))
|
||||
.setAutoRemoveAsync()
|
||||
.then(() => this.changeState('connected'))
|
||||
}
|
||||
|
||||
setAutoRemoveAsync() {
|
||||
const removeQuery = () => {
|
||||
return {expires: {$lt: new Date()}}
|
||||
}
|
||||
switch (this.autoRemove) {
|
||||
case 'native':
|
||||
return this.collection.createIndex({expires: 1}, {expireAfterSeconds: 0})
|
||||
case 'interval':
|
||||
this.timer = setInterval(() => this.collection.remove(removeQuery(), {w: 0}), this.autoRemoveInterval * 1000 * 60)
|
||||
this.timer.unref()
|
||||
return Promise.resolve()
|
||||
default:
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
changeState(newState) {
|
||||
if (newState !== this.state) {
|
||||
this.state = newState
|
||||
this.emit(newState)
|
||||
}
|
||||
}
|
||||
|
||||
setCollection(collection) {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
this.collectionReadyPromise = undefined
|
||||
this.collection = collection
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
collectionReady() {
|
||||
let promise = this.collectionReadyPromise
|
||||
if (!promise) {
|
||||
promise = new Promise((resolve, reject) => {
|
||||
if (this.state === 'connected') {
|
||||
return resolve(this.collection)
|
||||
}
|
||||
if (this.state === 'connecting') {
|
||||
return this.once('connected', () => resolve(this.collection))
|
||||
}
|
||||
reject(new Error('Not connected'))
|
||||
})
|
||||
this.collectionReadyPromise = promise
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
||||
computeStorageId(sessionId) {
|
||||
if (this.options.transformId && typeof this.options.transformId === 'function') {
|
||||
return this.options.transformId(sessionId)
|
||||
}
|
||||
return sessionId
|
||||
}
|
||||
|
||||
/* Public API */
|
||||
|
||||
get(sid, callback) {
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.findOne({
|
||||
_id: this.computeStorageId(sid),
|
||||
$or: [
|
||||
{expires: {$exists: false}},
|
||||
{expires: {$gt: new Date()}}
|
||||
]
|
||||
}))
|
||||
.then(session => {
|
||||
if (session) {
|
||||
const s = this.transformFunctions.unserialize(session.session)
|
||||
if (this.options.touchAfter > 0 && session.lastModified) {
|
||||
s.lastModified = session.lastModified
|
||||
}
|
||||
this.emit('get', sid)
|
||||
return s
|
||||
}
|
||||
})
|
||||
, callback)
|
||||
}
|
||||
|
||||
set(sid, session, callback) {
|
||||
// Removing the lastModified prop from the session object before update
|
||||
if (this.options.touchAfter > 0 && session && session.lastModified) {
|
||||
delete session.lastModified
|
||||
}
|
||||
|
||||
let s
|
||||
|
||||
try {
|
||||
s = {_id: this.computeStorageId(sid), session: this.transformFunctions.serialize(session)}
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
if (session && session.cookie && session.cookie.expires) {
|
||||
s.expires = new Date(session.cookie.expires)
|
||||
} else {
|
||||
// If there's no expiration date specified, it is
|
||||
// browser-session cookie or there is no cookie at all,
|
||||
// as per the connect docs.
|
||||
//
|
||||
// So we set the expiration to two-weeks from now
|
||||
// - as is common practice in the industry (e.g Django) -
|
||||
// or the default specified in the options.
|
||||
s.expires = new Date(Date.now() + (this.ttl * 1000))
|
||||
}
|
||||
|
||||
if (this.options.touchAfter > 0) {
|
||||
s.lastModified = new Date()
|
||||
}
|
||||
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.updateOne({_id: this.computeStorageId(sid)}, {$set: s}, {upsert: true}))
|
||||
.then(rawResponse => {
|
||||
if (rawResponse.result) {
|
||||
rawResponse = rawResponse.result
|
||||
}
|
||||
if (rawResponse && rawResponse.upserted) {
|
||||
this.emit('create', sid)
|
||||
} else {
|
||||
this.emit('update', sid)
|
||||
}
|
||||
this.emit('set', sid)
|
||||
})
|
||||
, callback)
|
||||
}
|
||||
|
||||
touch(sid, session, callback) {
|
||||
const updateFields = {}
|
||||
const touchAfter = this.options.touchAfter * 1000
|
||||
const lastModified = session.lastModified ? session.lastModified.getTime() : 0
|
||||
const currentDate = new Date()
|
||||
|
||||
// If the given options has a touchAfter property, check if the
|
||||
// current timestamp - lastModified timestamp is bigger than
|
||||
// the specified, if it's not, don't touch the session
|
||||
if (touchAfter > 0 && lastModified > 0) {
|
||||
const timeElapsed = currentDate.getTime() - session.lastModified
|
||||
|
||||
if (timeElapsed < touchAfter) {
|
||||
return callback()
|
||||
}
|
||||
updateFields.lastModified = currentDate
|
||||
}
|
||||
|
||||
if (session && session.cookie && session.cookie.expires) {
|
||||
updateFields.expires = new Date(session.cookie.expires)
|
||||
} else {
|
||||
updateFields.expires = new Date(Date.now() + (this.ttl * 1000))
|
||||
}
|
||||
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.updateOne({_id: this.computeStorageId(sid)}, {$set: updateFields}))
|
||||
.then(result => {
|
||||
if (result.nModified === 0) {
|
||||
throw new Error('Unable to find the session to touch')
|
||||
} else {
|
||||
this.emit('touch', sid, session)
|
||||
}
|
||||
})
|
||||
, callback)
|
||||
}
|
||||
|
||||
destroy(sid, callback) {
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.deleteOne({_id: this.computeStorageId(sid)}))
|
||||
.then(() => this.emit('destroy', sid))
|
||||
, callback)
|
||||
}
|
||||
|
||||
length(callback) {
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.count({}))
|
||||
, callback)
|
||||
}
|
||||
|
||||
clear(callback) {
|
||||
return withCallback(this.collectionReady()
|
||||
.then(collection => collection.drop())
|
||||
, callback)
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.db) {
|
||||
this.db.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MongoStore
|
||||
}
|
||||
Reference in New Issue
Block a user