UNPKG

connect-cosmosdb

Version:
249 lines (248 loc) 9.61 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const express_session_1 = require("express-session"); const noop = () => null; class CosmosStore extends express_session_1.Store { constructor() { super(); if (CosmosStore._store) throw new Error('Cosmos Store has already been initialized. Please use CosmosStore.initializeStore()'); if (!CosmosStore.options.cosmosClient) { throw new Error('Cannot initialize Cosmos Store. Please use CosmosStore.initializeStore()'); } } // Update static options static updateOptions(options) { CosmosStore.options.cosmosClient = options.cosmosClient; CosmosStore.options.containerName = options.containerName || CosmosStore.DEFAULT_CONTAINER_NAME; CosmosStore.options.databaseName = options.databaseName; CosmosStore.options.disableTouch = options.disableTouch || false; CosmosStore.options.ttl = options.ttl || CosmosStore.DEFAULT_TTL; //Default TTL is 1 day } /** * Initialize a Cosmos Store instance for Session Storage. * @param options Cosmos Store Options * @returns A Cosmos Store instance */ static initializeStore(options) { return __awaiter(this, void 0, void 0, function* () { if (!options.cosmosClient) throw new Error('Cosmos DB Client is required.'); if (!options.databaseName) throw new Error('Database name is required'); if (CosmosStore._store) { return CosmosStore._store; } CosmosStore.updateOptions(options); CosmosStore._store = new CosmosStore(); yield CosmosStore._store.setup(); return CosmosStore._store; }); } /** * * Get the session dat for the session ID * @param sid Session ID * @param callback callback function to call after execution */ get(sid, callback = noop) { return __awaiter(this, void 0, void 0, function* () { try { const { resource } = yield this.container .item(sid, sid) .read(); if (!resource) { return callback(null, null); } return callback(null, resource); } catch (error) { callback(error); } }); } /** * Set the session data for the session ID * @param sid Session ID * @param session Session data * @param callback callback function to call after execution */ set(sid, session, callback = noop) { return __awaiter(this, void 0, void 0, function* () { const currentTTL = this.getTTL(session); try { const sessionItem = Object.assign({ id: sid }, session); if (currentTTL) { //TTL is set or is computed from "Expires" value. if (currentTTL <= 0) { //TTL Expired return this.destroy(sid); } // Upsert session with TTL yield this.container.items.upsert(Object.assign(Object.assign({}, sessionItem), { ttl: currentTTL })); } else { // Upsert session without TTL yield this.container.items.create(Object.assign({}, sessionItem)); } callback(null); } catch (error) { callback(error); } }); } /** * Destroy a session. * * @param sid Session ID to destroy * @param callback callback function to call after execution */ destroy(sid, callback = noop) { return __awaiter(this, void 0, void 0, function* () { try { yield this.container.item(sid, sid).delete(); callback(null); } catch (error) { callback(error); } }); } /** * Get all sessions in the store * @param callback callback function to call after execution */ all(callback = noop) { return __awaiter(this, void 0, void 0, function* () { try { const { resources } = yield this.container.items.readAll().fetchAll(); callback(null, resources); } catch (error) { callback(error); } }); } /** Returns the number of sessions in the store. */ length(callback = noop) { return __awaiter(this, void 0, void 0, function* () { try { const { resources } = yield this.container.items .query({ query: `SELECT VALUE COUNT(s.id) FROM ${this.container.id} s`, }) .fetchAll(); const [count] = resources; //Result is a JSON array callback(null, count); } catch (error) { callback(error); } }); } /** Delete all sessions from the store. */ clear(callback = noop) { var _a; return __awaiter(this, void 0, void 0, function* () { try { const { containerName } = CosmosStore.options; yield ((_a = this.database) === null || _a === void 0 ? void 0 : _a.container(containerName).delete()); yield this.createContainer(); return callback(); } catch (error) { callback(error); } }); } /** "Touches" a given session, resetting the idle timer. */ touch(sid, session, callback = noop) { return __awaiter(this, void 0, void 0, function* () { if (CosmosStore.options.disableTouch) { return callback(); } try { // Note: Any write/update operation will update the TTL value in Cosmos. yield this.container.item(sid, sid).patch({ operations: [ { op: 'replace', path: '/ttl', value: this.getTTL(session), }, ], }); callback(); } catch (error) { callback(); } }); } // Initial Cosmos DB set-up setup() { return __awaiter(this, void 0, void 0, function* () { const { databaseName, containerName, cosmosClient } = CosmosStore.options; const { database } = yield cosmosClient.databases.createIfNotExists({ id: databaseName, }); this.database = database; yield this.createContainer(); this.container = this.database.container(containerName); }); } createContainer() { var _a; return __awaiter(this, void 0, void 0, function* () { const { containerName } = CosmosStore.options; // The TTL value can be overridden on individual items, if default TTL is non-null or -1. // TTL is disabled by default // If TTL was already defined (e.g. via Azure Portal/CLI), the predefined value would be used here, // as the container would have been created already. yield ((_a = this.database) === null || _a === void 0 ? void 0 : _a.containers.createIfNotExists({ id: containerName, defaultTtl: -1, partitionKey: CosmosStore.PARTITION_KEY_PATH, })); }); } /** * Get Time to live value in seconds for the session. * If the Expires value is set, TTL will be calculated based on Expiry. * Else the default value of 1 day would be used. * @param session * @returns TTL value in seconds (Cosmos DB uses seconds as TTL) */ getTTL(session) { // Custom expiry computation. if (typeof CosmosStore.options.ttl === 'function') return CosmosStore.options.ttl(session); if (session.cookie && session.cookie.expires) { const sessionExpiry = Number(new Date(session.cookie.expires)); const current = Date.now(); //Cosmos DB TTL in seconds return Math.ceil((sessionExpiry - current) / 1000); } return CosmosStore.options.ttl; } static reset() { CosmosStore._store = null; CosmosStore.options = {}; } } exports.default = CosmosStore; CosmosStore.options = {}; CosmosStore.PARTITION_KEY_PATH = '/id'; CosmosStore.DEFAULT_CONTAINER_NAME = 'sessions'; CosmosStore.DEFAULT_TTL = 86400;