Merge pull request #3332 from itzexor/memorystore-2

memorystore: simplify, refactor, re-enable
This commit is contained in:
advplyr 2024-08-27 16:56:07 -05:00 committed by GitHub
commit 1326d29fad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 58 additions and 241 deletions

View File

@ -41,6 +41,7 @@ const LibraryScanner = require('./scanner/LibraryScanner')
//Import the main Passport and Express-Session library //Import the main Passport and Express-Session library
const passport = require('passport') const passport = require('passport')
const expressSession = require('express-session') const expressSession = require('express-session')
const MemoryStore = require('./libs/memorystore')
class Server { class Server {
constructor(SOURCE, PORT, HOST, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) { constructor(SOURCE, PORT, HOST, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) {
@ -232,7 +233,8 @@ class Server {
cookie: { cookie: {
// also send the cookie if were are not on https (not every use has https) // also send the cookie if were are not on https (not every use has https)
secure: false secure: false
} },
store: new MemoryStore(86400000, 86400000, 1000)
}) })
) )
// init passport.js // init passport.js

View File

@ -8,89 +8,33 @@
// SOURCE: https://github.com/roccomuso/memorystore // SOURCE: https://github.com/roccomuso/memorystore
// //
var debug = require('debug')('memorystore') const debug = require('debug')('memorystore')
const { LRUCache } = require('lru-cache') const { LRUCache } = require('lru-cache')
var util = require('util') const { Store } = require('express-session')
/** /**
* One day in milliseconds. * An alternative memory store implementation for express session that prunes stale entries.
*/
var oneDay = 86400000
function getTTL(options, sess, sid) {
if (typeof options.ttl === 'number') return options.ttl
if (typeof options.ttl === 'function') return options.ttl(options, sess, sid)
if (options.ttl) throw new TypeError('`options.ttl` must be a number or function.')
var maxAge = sess?.cookie?.maxAge || null
return typeof maxAge === 'number' ? Math.floor(maxAge) : oneDay
}
function prune(store) {
debug('Pruning expired entries')
store.forEach(function (value, key) {
store.get(key)
})
}
var defer =
typeof setImmediate === 'function'
? setImmediate
: function (fn) {
process.nextTick(fn.bind.apply(fn, arguments))
}
/**
* Return the `MemoryStore` extending `express`'s session Store.
* *
* @param {object} express session * @param {number} checkPeriod stale entry pruning frequency in ms
* @return {Function} * @param {number} ttl entry time to live in ms
* @api public * @param {number} max LRU cache max entries
*/ */
module.exports = class MemoryStore extends Store {
module.exports = function (session) { constructor(checkPeriod, ttl, max) {
/** if (typeof checkPeriod !== 'number' || typeof ttl !== 'number' || typeof max !== 'number') {
* Express's session Store. throw Error('All arguments must be provided')
*/
var Store = session.Store
/**
* Initialize MemoryStore with the given `options`.
*
* @param {Object} options
* @api public
*/
function MemoryStore(options) {
if (!(this instanceof MemoryStore)) {
throw new TypeError('Cannot call MemoryStore constructor as a function')
} }
super()
options = options || {} this.store = new LRUCache({ ttl, max })
Store.call(this, options) let prune = () => {
let sizeBefore = this.store.size
this.options = {} this.store.purgeStale()
this.options.checkPeriod = options.checkPeriod debug('PRUNE size changed by %i entries', sizeBefore - this.store.size)
this.options.max = options.max }
this.options.ttl = options.ttl setInterval(prune, Math.floor(checkPeriod)).unref()
this.options.dispose = options.dispose debug('INIT MemoryStore constructed with checkPeriod "%i", ttl "%i", max "%i"', checkPeriod, ttl, max)
this.options.stale = options.stale
this.serializer = options.serializer || JSON
this.store = new LRUCache(this.options)
debug('Init MemoryStore')
this.startInterval()
} }
/**
* Inherit from `Store`.
*/
util.inherits(MemoryStore, Store)
/** /**
* Attempt to fetch session by the given `sid`. * Attempt to fetch session by the given `sid`.
* *
@ -98,25 +42,19 @@ module.exports = function (session) {
* @param {Function} fn * @param {Function} fn
* @api public * @api public
*/ */
get(sid, fn) {
MemoryStore.prototype.get = function (sid, fn) { let err = null
var store = this.store let res = null
const data = this.store.get(sid)
debug('GET "%s"', sid) debug('GET %s: %s', sid, data)
if (data) {
var data = store.get(sid) try {
if (!data) return fn() res = JSON.parse(data)
} catch (e) {
debug('GOT %s', data) err = e
var err = null }
var result
try {
result = this.serializer.parse(data)
} catch (er) {
err = er
} }
fn && setImmediate(fn, err, res)
fn && defer(fn, err, result)
} }
/** /**
@ -127,48 +65,39 @@ module.exports = function (session) {
* @param {Function} fn * @param {Function} fn
* @api public * @api public
*/ */
set(sid, sess, fn) {
MemoryStore.prototype.set = function (sid, sess, fn) { let err = null
var store = this.store
var ttl = getTTL(this.options, sess, sid)
try { try {
var jsess = this.serializer.stringify(sess) let jsess = JSON.stringify(sess)
} catch (err) { debug('SET %s: %s', sid, jsess)
fn && defer(fn, err) this.store.set(sid, jsess)
} catch (e) {
err = e
} }
fn && setImmediate(fn, err)
store.set(sid, jsess, {
ttl
})
debug('SET "%s" %s ttl:%s', sid, jsess, ttl)
fn && defer(fn, null)
} }
/** /**
* Destroy the session associated with the given `sid`. * Destroy the session associated with the given `sid`.
* *
* @param {String} sid * @param {String} sid
* @param {Function} fn
* @api public * @api public
*/ */
destroy(sid, fn) {
MemoryStore.prototype.destroy = function (sid, fn) { debug('DESTROY %s', sid)
var store = this.store let err = null
try {
if (Array.isArray(sid)) { this.store.delete(sid)
sid.forEach(function (s) { } catch (e) {
debug('DEL "%s"', s) err = e
store.delete(s)
})
} else {
debug('DEL "%s"', sid)
store.delete(sid)
} }
fn && defer(fn, null) fn && setImmediate(fn, err)
} }
/** /**
* Refresh the time-to-live for the session with the given `sid`. * Refresh the time-to-live for the session with the given `sid` without affecting
* LRU recency.
* *
* @param {String} sid * @param {String} sid
* @param {Session} sess * @param {Session} sess
@ -176,128 +105,14 @@ module.exports = function (session) {
* @api public * @api public
*/ */
MemoryStore.prototype.touch = function (sid, sess, fn) { touch(sid, sess, fn) {
var store = this.store debug('TOUCH %s', sid)
let err = null
var ttl = getTTL(this.options, sess, sid)
debug('EXPIRE "%s" ttl:%s', sid, ttl)
var err = null
if (store.get(sid) !== undefined) {
try {
var s = this.serializer.parse(store.get(sid))
s.cookie = sess.cookie
store.set(sid, this.serializer.stringify(s), {
ttl
})
} catch (e) {
err = e
}
}
fn && defer(fn, err)
}
/**
* Fetch all sessions' ids
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.ids = function (fn) {
var store = this.store
var Ids = store.keys()
debug('Getting IDs: %s', Ids)
fn && defer(fn, null, Ids)
}
/**
* Fetch all sessions
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.all = function (fn) {
var store = this.store
var self = this
debug('Fetching all sessions')
var err = null
var result = {}
try { try {
store.forEach(function (val, key) { this.store.has(sid, { updateAgeOnHas: true })
result[key] = self.serializer.parse(val)
})
} catch (e) { } catch (e) {
err = e err = e
} }
fn && defer(fn, err, result) fn && setImmediate(fn, err)
} }
/**
* Delete all sessions from the store
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.clear = function (fn) {
var store = this.store
debug('delete all sessions from the store')
store.clear()
fn && defer(fn, null)
}
/**
* Get the count of all sessions in the store
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.length = function (fn) {
var store = this.store
debug('getting length', store.size)
fn && defer(fn, null, store.size)
}
/**
* Start the check interval
* @api public
*/
MemoryStore.prototype.startInterval = function () {
var self = this
var ms = this.options.checkPeriod
if (ms && typeof ms === 'number') {
clearInterval(this._checkInterval)
debug('starting periodic check for expired sessions')
this._checkInterval = setInterval(function () {
prune(self.store) // iterates over the entire cache proactively pruning old entries
}, Math.floor(ms)).unref()
}
}
/**
* Stop the check interval
* @api public
*/
MemoryStore.prototype.stopInterval = function () {
debug('stopping periodic check for expired sessions')
clearInterval(this._checkInterval)
}
/**
* Remove only expired entries from the store
* @api public
*/
MemoryStore.prototype.prune = function () {
prune(this.store)
}
return MemoryStore
} }