import { SessionAlreadyExists , SessionState , SessionStore } from '@foal/core' ; import { Column , Entity , getRepository , IsNull , LessThan , Not , PrimaryColumn } from 'typeorm' ; @ Entity ( { name : 'sessions' } ) export class DatabaseSession { @ PrimaryColumn ( { length : 44 } ) id : string ; @ Column ( { nullable : true } ) user_id : number ; @ Column ( { type : 'text' } ) content : string ; @ Column ( { type : 'text' } ) flash : string ; @ Column ( ) updated_at : number ; @ Column ( ) created_at : number ; } export class extends SessionStore export class TypeORMStore extends SessionStore { 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 getRepository ( DatabaseSession ) . createQueryBuilder ( ) . insert ( ) . values ( { content : JSON . stringify ( state . content ) , created_at : state . createdAt , flash : JSON . stringify ( state . flash ) , id : state . id , updated_at : state . updatedAt , user_id : state . userId ?? undefined , } ) . execute ( ) ; } catch ( error ) { if ( [ 'SQLITE_CONSTRAINT' , 'ER_DUP_ENTRY' , '23505' ] . includes ( error . code ) || error . message === 'SqliteError: UNIQUE constraint failed: sessions.id' ) { throw new SessionAlreadyExists ( ) ; } throw error ; } } async read ( id ) { const session = await getRepository ( DatabaseSession ) . findOne ( { 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 , 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 = getRepository ( DatabaseSession ) . create ( { content : JSON . stringify ( state . content ) , created_at : state . createdAt , flash : JSON . stringify ( state . flash ) , id : state . id , updated_at : state . updatedAt , user_id : state . userId ?? undefined , } ) ; await getRepository ( DatabaseSession ) . save ( dbSession ) ; } async destroy ( sessionID ) { await getRepository ( DatabaseSession ) . delete ( { id : sessionID } ) ; } async clear ( ) { await getRepository ( DatabaseSession ) . clear ( ) ; } async cleanUpExpiredSessions ( maxInactivity , maxLifeTime ) { await getRepository ( DatabaseSession ) . createQueryBuilder ( ) . delete ( ) . where ( [ { created_at : LessThan ( Math . trunc ( Date . now ( ) / 1000 ) - maxLifeTime ) } , { updated_at : LessThan ( Math . trunc ( Date . now ( ) / 1000 ) - maxInactivity ) } , ] ) . execute ( ) ; } async getAuthenticatedUserIds ( ) { const sessions = await getRepository ( DatabaseSession ) . createQueryBuilder ( ) . select ( 'DISTINCT user_id' ) . where ( { user_id : Not ( IsNull ( ) ) } ) . getRawMany ( ) ; return sessions . map ( ( { user_id } ) => user_id ) ; } async destroyAllSessionsOf ( user ) { await getRepository ( DatabaseSession ) . delete ( { user_id : user . id } ) ; } async getSessionIDsOf ( user ) { const databaseSessions = await getRepository ( DatabaseSession ) . find ( { select : [ 'id' ] , where : { user_id : user . id } , } ) ; return databaseSessions . map ( dbSession => dbSession . id ) ; } }