mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Merge pull request #3332 from itzexor/memorystore-2
memorystore: simplify, refactor, re-enable
This commit is contained in:
commit
1326d29fad
@ -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
|
||||||
|
@ -8,88 +8,32 @@
|
|||||||
// 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)
|
|
||||||
if (!data) return fn()
|
|
||||||
|
|
||||||
debug('GOT %s', data)
|
|
||||||
var err = null
|
|
||||||
var result
|
|
||||||
try {
|
try {
|
||||||
result = this.serializer.parse(data)
|
res = JSON.parse(data)
|
||||||
} catch (er) {
|
} catch (e) {
|
||||||
err = er
|
err = e
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn && defer(fn, err, result)
|
fn && setImmediate(fn, err, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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 {
|
try {
|
||||||
var s = this.serializer.parse(store.get(sid))
|
this.store.has(sid, { updateAgeOnHas: true })
|
||||||
s.cookie = sess.cookie
|
|
||||||
store.set(sid, this.serializer.stringify(s), {
|
|
||||||
ttl
|
|
||||||
})
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e
|
err = e
|
||||||
}
|
}
|
||||||
|
fn && setImmediate(fn, err)
|
||||||
}
|
}
|
||||||
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 {
|
|
||||||
store.forEach(function (val, key) {
|
|
||||||
result[key] = self.serializer.parse(val)
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
err = e
|
|
||||||
}
|
|
||||||
fn && defer(fn, err, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user