UNPKG

connect-surreal

Version:

SurrealDB session store for Connect

133 lines 5.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SurrealDBStore = void 0; const express_session_1 = require("express-session"); const surrealdb_1 = require("surrealdb"); class SurrealDBStore extends express_session_1.Store { constructor(options) { super(); this.options = options; this.lastConnectionAttempt = 0; this.hasConnected = false; this.isConnected = false; this.db = options.surreal ?? new surrealdb_1.Surreal(); // TODO: support provided error handler this.db.emitter.subscribe('error', console.log); // Re-connect this.db.emitter.subscribe("disconnected", () => { this.isConnected = false; this._reconnect(); }); // Preventative for SQLi if the developer hasn't hardcoded this. if (options.tableName && /^[a-zA-Z0-9]+$/.test(options.tableName)) throw new Error("Invalid table name."); this.tableName = options.tableName ?? 'user_sessions'; this._connect().catch(err => { console.error("Failed to connect express-session SurrealDB Store to database!\n" + err.message + '\n' + err.stack); }); } /** * Perform the initial connection to the database. This also sets the scope of our connection. */ async _connect() { await this.db.connect(this.options.url, this.options.connectionOpts); await this.db.signin(this.options.signinOpts); if (this.options.useOpts) { await this.db.use(this.options.useOpts); } this.isConnected = true; } /** * Reconnect to the database if our connection drops. This uses a connection * throttling technique to prevent connection storming. */ async _reconnect() { if (this.isConnected) return; // If this last tried to connect under 5 seconds ago, abort. // This prevents overloading the network with connection attempts. if (Date.now() - this.lastConnectionAttempt < 5000) { if (this.hasConnected) { console.error("The connection to SurrealDB appears to have dropped."); } else { console.error("Cannot reconnect to SurrealDB."); } return; } this.lastConnectionAttempt = Date.now(); await this.db.connect(this.options.url, this.options.connectionOpts) .then(() => { this.isConnected = true; }); await this.db.signin(this.options.signinOpts); if (this.options.useOpts) await this.db.use(this.options.useOpts); } /** * Check the connection state and attempt to reconnect before continuing * This ensures that sessions shouldn't observe disruptions in edge cases * where the connection gets lost and we can't connect immediately. * * If there's 50 minutes between the drop and a new connection, the user won't * get an error screen and need to refresh their page. */ async _checkConnectionAndReconnect() { if (!this.isConnected) { if (this.hasConnected) { // The connection has dropped once and we just need to reconnect. await this._reconnect(); } else { // In the case where the initial connection // fails and is resolved after startup. await this._connect(); } } } get(sessionId, cb) { this._checkConnectionAndReconnect() .then(() => { this.db.select(new surrealdb_1.RecordId(this.tableName, sessionId)) .then((res) => cb(null, res)) .catch(err => cb(err)); }) .catch(err => cb(err)); } set(sessionId, session, cb) { this._checkConnectionAndReconnect() .then(() => { this.db.upsert(new surrealdb_1.RecordId(this.tableName, sessionId), session) .then((res) => cb(null, res)) .catch(err => cb(err)); }) .catch(err => cb(err)); } touch(sid, session, cb) { // TODO: The schema of the table should be automatically // generated and should have a TTL on sessions this.set(sid, session, cb); } destroy(sessionId, cb) { this.db.delete(new surrealdb_1.RecordId(this.tableName, sessionId)) .then(() => cb(null)) .catch(err => cb(err)); } length(cb) { this.db.query(`SELECT count() from $p group by count`, { 'p': this.tableName }) .then(([result]) => cb(result[0].count)) .catch(err => cb(err)); } all(cb) { this.db.select(this.tableName) .then(([result]) => cb(result)) .catch(err => cb(err)); } clear(cb) { this.db.query(`DELETE $p`, { 'p': this.tableName }) .then(() => cb(null)) .catch(err => cb(err)); } } exports.SurrealDBStore = SurrealDBStore; //# sourceMappingURL=main.js.map