added session storage to application

This commit is contained in:
2025-07-07 22:56:04 +02:00
parent dda29ac15c
commit 97e749630e
53 changed files with 5853 additions and 1 deletions

View File

@@ -0,0 +1,152 @@
/*!
* Connect - session - Cookie
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var cookie = require('cookie')
var deprecate = require('depd')('express-session')
/**
* Initialize a new `Cookie` with the given `options`.
*
* @param {IncomingMessage} req
* @param {Object} options
* @api private
*/
var Cookie = module.exports = function Cookie(options) {
this.path = '/';
this.maxAge = null;
this.httpOnly = true;
if (options) {
if (typeof options !== 'object') {
throw new TypeError('argument options must be a object')
}
for (var key in options) {
if (key !== 'data') {
this[key] = options[key]
}
}
}
if (this.originalMaxAge === undefined || this.originalMaxAge === null) {
this.originalMaxAge = this.maxAge
}
};
/*!
* Prototype.
*/
Cookie.prototype = {
/**
* Set expires `date`.
*
* @param {Date} date
* @api public
*/
set expires(date) {
this._expires = date;
this.originalMaxAge = this.maxAge;
},
/**
* Get expires `date`.
*
* @return {Date}
* @api public
*/
get expires() {
return this._expires;
},
/**
* Set expires via max-age in `ms`.
*
* @param {Number} ms
* @api public
*/
set maxAge(ms) {
if (ms && typeof ms !== 'number' && !(ms instanceof Date)) {
throw new TypeError('maxAge must be a number or Date')
}
if (ms instanceof Date) {
deprecate('maxAge as Date; pass number of milliseconds instead')
}
this.expires = typeof ms === 'number'
? new Date(Date.now() + ms)
: ms;
},
/**
* Get expires max-age in `ms`.
*
* @return {Number}
* @api public
*/
get maxAge() {
return this.expires instanceof Date
? this.expires.valueOf() - Date.now()
: this.expires;
},
/**
* Return cookie data object.
*
* @return {Object}
* @api private
*/
get data() {
return {
originalMaxAge: this.originalMaxAge,
partitioned: this.partitioned,
priority: this.priority
, expires: this._expires
, secure: this.secure
, httpOnly: this.httpOnly
, domain: this.domain
, path: this.path
, sameSite: this.sameSite
}
},
/**
* Return a serialized cookie string.
*
* @return {String}
* @api public
*/
serialize: function(name, val){
return cookie.serialize(name, val, this.data);
},
/**
* Return JSON representation of this cookie.
*
* @return {Object}
* @api private
*/
toJSON: function(){
return this.data;
}
};

View File

@@ -0,0 +1,187 @@
/*!
* express-session
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var Store = require('./store')
var util = require('util')
/**
* Shim setImmediate for node.js < 0.10
* @private
*/
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
/**
* Module exports.
*/
module.exports = MemoryStore
/**
* A session store in memory.
* @public
*/
function MemoryStore() {
Store.call(this)
this.sessions = Object.create(null)
}
/**
* Inherit from Store.
*/
util.inherits(MemoryStore, Store)
/**
* Get all active sessions.
*
* @param {function} callback
* @public
*/
MemoryStore.prototype.all = function all(callback) {
var sessionIds = Object.keys(this.sessions)
var sessions = Object.create(null)
for (var i = 0; i < sessionIds.length; i++) {
var sessionId = sessionIds[i]
var session = getSession.call(this, sessionId)
if (session) {
sessions[sessionId] = session;
}
}
callback && defer(callback, null, sessions)
}
/**
* Clear all sessions.
*
* @param {function} callback
* @public
*/
MemoryStore.prototype.clear = function clear(callback) {
this.sessions = Object.create(null)
callback && defer(callback)
}
/**
* Destroy the session associated with the given session ID.
*
* @param {string} sessionId
* @public
*/
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
delete this.sessions[sessionId]
callback && defer(callback)
}
/**
* Fetch session by the given session ID.
*
* @param {string} sessionId
* @param {function} callback
* @public
*/
MemoryStore.prototype.get = function get(sessionId, callback) {
defer(callback, null, getSession.call(this, sessionId))
}
/**
* Commit the given session associated with the given sessionId to the store.
*
* @param {string} sessionId
* @param {object} session
* @param {function} callback
* @public
*/
MemoryStore.prototype.set = function set(sessionId, session, callback) {
this.sessions[sessionId] = JSON.stringify(session)
callback && defer(callback)
}
/**
* Get number of active sessions.
*
* @param {function} callback
* @public
*/
MemoryStore.prototype.length = function length(callback) {
this.all(function (err, sessions) {
if (err) return callback(err)
callback(null, Object.keys(sessions).length)
})
}
/**
* Touch the given session object associated with the given session ID.
*
* @param {string} sessionId
* @param {object} session
* @param {function} callback
* @public
*/
MemoryStore.prototype.touch = function touch(sessionId, session, callback) {
var currentSession = getSession.call(this, sessionId)
if (currentSession) {
// update expiration
currentSession.cookie = session.cookie
this.sessions[sessionId] = JSON.stringify(currentSession)
}
callback && defer(callback)
}
/**
* Get session from the store.
* @private
*/
function getSession(sessionId) {
var sess = this.sessions[sessionId]
if (!sess) {
return
}
// parse
sess = JSON.parse(sess)
if (sess.cookie) {
var expires = typeof sess.cookie.expires === 'string'
? new Date(sess.cookie.expires)
: sess.cookie.expires
// destroy expired session
if (expires && expires <= Date.now()) {
delete this.sessions[sessionId]
return
}
}
return sess
}

View File

@@ -0,0 +1,143 @@
/*!
* Connect - session - Session
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
'use strict';
/**
* Expose Session.
*/
module.exports = Session;
/**
* Create a new `Session` with the given request and `data`.
*
* @param {IncomingRequest} req
* @param {Object} data
* @api private
*/
function Session(req, data) {
Object.defineProperty(this, 'req', { value: req });
Object.defineProperty(this, 'id', { value: req.sessionID });
if (typeof data === 'object' && data !== null) {
// merge data into this, ignoring prototype properties
for (var prop in data) {
if (!(prop in this)) {
this[prop] = data[prop]
}
}
}
}
/**
* Update reset `.cookie.maxAge` to prevent
* the cookie from expiring when the
* session is still active.
*
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'touch', function touch() {
return this.resetMaxAge();
});
/**
* Reset `.maxAge` to `.originalMaxAge`.
*
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
this.cookie.maxAge = this.cookie.originalMaxAge;
return this;
});
/**
* Save the session data with optional callback `fn(err)`.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'save', function save(fn) {
this.req.sessionStore.set(this.id, this, fn || function(){});
return this;
});
/**
* Re-loads the session data _without_ altering
* the maxAge properties. Invokes the callback `fn(err)`,
* after which time if no exception has occurred the
* `req.session` property will be a new `Session` object,
* although representing the same session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'reload', function reload(fn) {
var req = this.req
var store = this.req.sessionStore
store.get(this.id, function(err, sess){
if (err) return fn(err);
if (!sess) return fn(new Error('failed to load session'));
store.createSession(req, sess);
fn();
});
return this;
});
/**
* Destroy `this` session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
delete this.req.session;
this.req.sessionStore.destroy(this.id, fn);
return this;
});
/**
* Regenerate this request's session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
defineMethod(Session.prototype, 'regenerate', function regenerate(fn) {
this.req.sessionStore.regenerate(this.req, fn);
return this;
});
/**
* Helper function for creating a method on a prototype.
*
* @param {Object} obj
* @param {String} name
* @param {Function} fn
* @private
*/
function defineMethod(obj, name, fn) {
Object.defineProperty(obj, name, {
configurable: true,
enumerable: false,
value: fn,
writable: true
});
};

View File

@@ -0,0 +1,102 @@
/*!
* Connect - session - Store
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var Cookie = require('./cookie')
var EventEmitter = require('events').EventEmitter
var Session = require('./session')
var util = require('util')
/**
* Module exports.
* @public
*/
module.exports = Store
/**
* Abstract base class for session stores.
* @public
*/
function Store () {
EventEmitter.call(this)
}
/**
* Inherit from EventEmitter.
*/
util.inherits(Store, EventEmitter)
/**
* Re-generate the given requests's session.
*
* @param {IncomingRequest} req
* @return {Function} fn
* @api public
*/
Store.prototype.regenerate = function(req, fn){
var self = this;
this.destroy(req.sessionID, function(err){
self.generate(req);
fn(err);
});
};
/**
* Load a `Session` instance via the given `sid`
* and invoke the callback `fn(err, sess)`.
*
* @param {String} sid
* @param {Function} fn
* @api public
*/
Store.prototype.load = function(sid, fn){
var self = this;
this.get(sid, function(err, sess){
if (err) return fn(err);
if (!sess) return fn();
var req = { sessionID: sid, sessionStore: self };
fn(null, self.createSession(req, sess))
});
};
/**
* Create session from JSON `sess` data.
*
* @param {IncomingRequest} req
* @param {Object} sess
* @return {Session}
* @api private
*/
Store.prototype.createSession = function(req, sess){
var expires = sess.cookie.expires
var originalMaxAge = sess.cookie.originalMaxAge
sess.cookie = new Cookie(sess.cookie);
if (typeof expires === 'string') {
// convert expires to a Date object
sess.cookie.expires = new Date(expires)
}
// keep originalMaxAge intact
sess.cookie.originalMaxAge = originalMaxAge
req.session = new Session(req, sess);
return req.session;
};