mirror of
https://github.com/Terreii/andromeda-viewer.git
synced 2026-08-14 00:57:49 +00:00
140 lines
3.9 KiB
JavaScript
140 lines
3.9 KiB
JavaScript
'use strict'
|
|
|
|
module.exports = init
|
|
|
|
const pouchdbErrors = require('pouchdb-errors')
|
|
const ms = require('milliseconds')
|
|
const uuid = require('uuid').v4
|
|
|
|
/**
|
|
* Setup the Grid session methods.
|
|
* @param {import('express').Application} app
|
|
*/
|
|
function init (app) {
|
|
// Stores the active session ids and their state.
|
|
// The state can be:
|
|
// - {number} Date.now() of when the last activity. But the check returns 'inactive'
|
|
// - 'active' when there is an open WebSocket connection.
|
|
//
|
|
// When the user logs in the session will be generated and the date will be stored.
|
|
// If the user then connects, before the timeout, then the state will be switched to 'active'.
|
|
// When a WebSocket disconnects, then the time of disconnect will be stored.
|
|
// And after a timeout the session will be deleted.
|
|
const sessions = new Map()
|
|
const onTimeoutSessionIds = new Set()
|
|
app.set('gridSessions', sessions)
|
|
app.set('gridSessionsIdsOnTimeout', onTimeoutSessionIds)
|
|
|
|
app.set('generateSession', generate.bind(null, app))
|
|
app.set('checkSession', check.bind(null, app))
|
|
app.set('changeSessionState', change.bind(null, app))
|
|
}
|
|
|
|
/**
|
|
* Generates a new session UUID.
|
|
* @param {import('express').Application} app Express Server App
|
|
* @returns {string} A new session UUID.
|
|
*/
|
|
function generate (app) {
|
|
const id = uuid()
|
|
const sessions = app.get('gridSessions')
|
|
const timeoutIds = app.get('gridSessionsIdsOnTimeout')
|
|
sessions.set(id, Date.now())
|
|
timeoutIds.add(id)
|
|
return id
|
|
}
|
|
|
|
/**
|
|
* Check if the session is active.
|
|
* @param {import('express').Application} app Express Server App
|
|
* @param {string} id UUID of the session.
|
|
* @returns {'active'|'inactive|} State of the session. Number = timeout & no active connection.
|
|
*/
|
|
function check (app, id) {
|
|
const sessions = app.get('gridSessions')
|
|
const timeoutIds = app.get('gridSessionsIdsOnTimeout')
|
|
|
|
if (!sessions.has(id)) {
|
|
throw pouchdbErrors.createError(
|
|
pouchdbErrors.UNAUTHORIZED,
|
|
'"x-andromeda-session-id" is wrong'
|
|
)
|
|
}
|
|
|
|
// Check if sessions did timeout and remove some of them.
|
|
process.nextTick(() => {
|
|
const timeout = Date.now() - ms.minutes(10)
|
|
const toRemove = []
|
|
for (const sessionId of timeoutIds) {
|
|
if (sessions.get(sessionId) < timeout) {
|
|
toRemove.push(sessionId)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if (toRemove.length > 0) {
|
|
for (const sessionId of toRemove) {
|
|
sessions.delete(sessionId)
|
|
timeoutIds.delete(sessionId)
|
|
}
|
|
}
|
|
})
|
|
|
|
const state = sessions.get(id)
|
|
if (typeof state === 'number' && timeoutIds.has(id) && state < (Date.now() - ms.minutes(10))) {
|
|
sessions.delete(id)
|
|
timeoutIds.delete(id)
|
|
throw pouchdbErrors.createError(
|
|
pouchdbErrors.UNAUTHORIZED,
|
|
'"x-andromeda-session-id" is wrong'
|
|
)
|
|
}
|
|
return typeof state === 'number' ? 'inactive' : state
|
|
}
|
|
|
|
/**
|
|
* Change the session state
|
|
* @param {import('express').Application} app Express Server App
|
|
* @param {string} id UUID of the session
|
|
* @param {'active'|'inactive'|'end'} state Session is active (has a connection), inactive or did end
|
|
* @returns {'active'|'end'|number} The next state.
|
|
*/
|
|
function change (app, id, state) {
|
|
const sessions = app.get('gridSessions')
|
|
const timeoutIds = app.get('gridSessionsIdsOnTimeout')
|
|
|
|
if (!sessions.has(id)) {
|
|
throw pouchdbErrors.createError(
|
|
pouchdbErrors.UNAUTHORIZED,
|
|
'"x-andromeda-session-id" is wrong'
|
|
)
|
|
}
|
|
|
|
switch (state) {
|
|
case 'end':
|
|
sessions.delete(id)
|
|
if (timeoutIds.has(id)) {
|
|
timeoutIds.delete(id)
|
|
}
|
|
return 'end'
|
|
|
|
case 'active':
|
|
sessions.set(id, 'active')
|
|
if (timeoutIds.has(id)) {
|
|
timeoutIds.delete(id)
|
|
}
|
|
return 'active'
|
|
|
|
case 'inactive':
|
|
{
|
|
const now = Date.now()
|
|
sessions.set(id, now)
|
|
timeoutIds.add(id)
|
|
return 'inactive'
|
|
}
|
|
|
|
default:
|
|
throw pouchdbErrors.createError(pouchdbErrors.INVALID_REQUEST, `bad state "${state}"`)
|
|
}
|
|
}
|