import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { IsNull, LessThan, Repository } from 'typeorm'; import { SessionEntity } from '../entities/session.entity'; @Injectable() export class SessionsRepository { constructor( @InjectRepository(SessionEntity) private readonly repo: Repository, ) {} findActiveById(id: string): Promise { return this.repo.findOne({ where: { id, revokedAt: IsNull() }, relations: { user: { roles: { permissions: true }, settings: true } }, }); } listForUser(userId: string): Promise { return this.repo.find({ where: { userId }, order: { createdAt: 'DESC' } }); } listActiveForUser(userId: string): Promise { return this.repo.find({ where: { userId, revokedAt: IsNull() }, order: { lastActivityAt: 'DESC' }, }); } countActiveForUser(userId: string): Promise { return this.repo.count({ where: { userId, revokedAt: IsNull() } }); } save(session: SessionEntity): Promise { return this.repo.save(session); } async revoke(sessionId: string): Promise { const result = await this.repo.update( { id: sessionId, revokedAt: IsNull() }, { revokedAt: new Date() }, ); return result.affected ?? 0; } async revokeForUser(userId: string, sessionId: string): Promise { const result = await this.repo.update( { id: sessionId, userId, revokedAt: IsNull() }, { revokedAt: new Date() }, ); return result.affected ?? 0; } async revokeAllForUser( userId: string, exceptSessionId?: string, ): Promise { const sessions = await this.repo.find({ where: { userId, revokedAt: IsNull() }, }); const now = new Date(); const targets = sessions.filter( (session) => session.id !== exceptSessionId, ); await this.repo.save( targets.map((session) => ({ ...session, revokedAt: now })), ); return targets.length; } async cleanupExpired(now = new Date()): Promise { await this.repo.delete([ { expiresAt: LessThan(now) }, { absoluteExpiresAt: LessThan(now) }, ]); } }