UNPKG

@foal/typeorm

Version:

FoalTS integration of TypeORM

181 lines (180 loc) 6.79 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeORMStore = exports.DatabaseSession = void 0; // 3p const core_1 = require("@foal/core"); const typeorm_1 = require("typeorm"); let DatabaseSession = class DatabaseSession extends typeorm_1.BaseEntity { id; // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name user_id; content; flash; // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name updated_at; // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name created_at; }; exports.DatabaseSession = DatabaseSession; __decorate([ (0, typeorm_1.PrimaryColumn)({ length: 44 }), __metadata("design:type", String) ], DatabaseSession.prototype, "id", void 0); __decorate([ (0, typeorm_1.Column)({ nullable: true }) // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name , __metadata("design:type", Number) ], DatabaseSession.prototype, "user_id", void 0); __decorate([ (0, typeorm_1.Column)({ type: 'text' }), __metadata("design:type", String) ], DatabaseSession.prototype, "content", void 0); __decorate([ (0, typeorm_1.Column)({ type: 'text' }), __metadata("design:type", String) ], DatabaseSession.prototype, "flash", void 0); __decorate([ (0, typeorm_1.Column)() // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name , __metadata("design:type", Number) ], DatabaseSession.prototype, "updated_at", void 0); __decorate([ (0, typeorm_1.Column)() // Use snake case because camelCase does not work well with PostgreSQL. // tslint:disable-next-line: variable-name , __metadata("design:type", Number) ], DatabaseSession.prototype, "created_at", void 0); exports.DatabaseSession = DatabaseSession = __decorate([ (0, typeorm_1.Entity)({ name: 'sessions' }) ], DatabaseSession); /** * TypeORM store. * * @export * @class TypeORMStore * @extends {SessionStore} */ class TypeORMStore extends core_1.SessionStore { get repository() { return DatabaseSession.getRepository(); } async save(state, maxInactivity) { if (typeof state.userId === 'string') { throw new Error('[TypeORMStore] Impossible to save the session. The user ID must be a number.'); } try { await this.repository .createQueryBuilder() .insert() .values({ content: JSON.stringify(state.content), created_at: state.createdAt, flash: JSON.stringify(state.flash), id: state.id, updated_at: state.updatedAt, // tslint:disable-next-line user_id: state.userId ?? undefined, }) .execute(); } catch (error) { // SQLite, MySQL, PostgreSQL if (['SQLITE_CONSTRAINT', 'ER_DUP_ENTRY', '23505'].includes(error.code) || error.message === 'SqliteError: UNIQUE constraint failed: sessions.id') { throw new core_1.SessionAlreadyExists(); } // TODO: test this line. throw error; } } async read(id) { const session = await this.repository.findOneBy({ id }); if (!session) { return null; } return { content: JSON.parse(session.content), createdAt: session.created_at, flash: JSON.parse(session.flash), id: session.id, updatedAt: session.updated_at, // Note: session.user_id is actually a number or null (not undefined). userId: session.user_id, }; } async update(state, maxInactivity) { if (typeof state.userId === 'string') { throw new Error('[TypeORMStore] Impossible to save the session. The user ID must be a number.'); } const dbSession = this.repository.create({ content: JSON.stringify(state.content), created_at: state.createdAt, flash: JSON.stringify(state.flash), id: state.id, updated_at: state.updatedAt, // tslint:disable-next-line user_id: state.userId ?? undefined, }); // The "save" method performs an UPSERT. await this.repository.save(dbSession); } async destroy(sessionID) { await this.repository .delete({ id: sessionID }); } async clear() { await this.repository .clear(); } async cleanUpExpiredSessions(maxInactivity, maxLifeTime) { await this.repository .createQueryBuilder() .delete() .where([ { created_at: (0, typeorm_1.LessThan)(Math.trunc(Date.now() / 1000) - maxLifeTime) }, { updated_at: (0, typeorm_1.LessThan)(Math.trunc(Date.now() / 1000) - maxInactivity) }, ]) .execute(); } async getAuthenticatedUserIds() { const sessions = await this.repository .createQueryBuilder() .select('DISTINCT user_id') .where({ user_id: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()) }) .getRawMany(); return sessions.map(({ user_id }) => user_id); } async destroyAllSessionsOf(userId) { await this.repository.delete({ user_id: userId }); } async getSessionIDsOf(userId) { const databaseSessions = await this.repository.find({ // Do not select unused fields. select: { id: true }, where: { user_id: userId }, }); return databaseSessions.map(dbSession => dbSession.id); } } exports.TypeORMStore = TypeORMStore;