1+ /* eslint-disable @typescript-eslint/no-explicit-any */
12import type { FastifyInstance , FastifyReply , FastifyRequest } from 'fastify' ;
23import { getLucia } from '../../lib/lucia' ;
34import { getDb , getSchema , getDbStatus } from '../../db' ;
45import { eq } from 'drizzle-orm' ;
56import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3' ;
67import { z } from 'zod' ;
78import { createSchema } from 'zod-openapi' ;
9+ import { EVENT_NAMES } from '../../events' ;
10+ import type { EventContext } from '../../events/types' ;
811
912// Zod schema for the logout response
1013const logoutResponseSchema = z . object ( {
@@ -58,7 +61,7 @@ export default async function logoutRoute(fastify: FastifyInstance) {
5861 if ( authSessionTable && authSessionTable . id ) {
5962 const dbStatus = getDbStatus ( ) ;
6063 if ( dbStatus . dialect === 'sqlite' ) {
61- // eslint-disable-next-line @typescript-eslint/no-explicit-any
64+
6265 const sqliteDb = db as BetterSQLite3Database < any > ;
6366 await sqliteDb . delete ( authSessionTable ) . where ( eq ( authSessionTable . id , sessionId ) ) ;
6467 }
@@ -94,6 +97,67 @@ export default async function logoutRoute(fastify: FastifyInstance) {
9497 reply . setCookie ( blankCookie . name , blankCookie . value , blankCookie . attributes ) ;
9598 fastify . log . info ( 'Blank cookie sent to clear client session' ) ;
9699
100+ // Emit USER_LOGOUT event
101+ try {
102+ if ( request . user ) {
103+ // Get user role from database since it's not in the session
104+ const db = getDb ( ) ;
105+ const schema = getSchema ( ) ;
106+ const authUserTable = schema . authUser ;
107+ let userRole = 'unknown' ;
108+
109+ try {
110+ const userResult = await ( db as any )
111+ . select ( { role_id : authUserTable . role_id } )
112+ . from ( authUserTable )
113+ . where ( eq ( authUserTable . id , request . user . id ) )
114+ . limit ( 1 ) ;
115+
116+ if ( userResult . length > 0 ) {
117+ userRole = userResult [ 0 ] . role_id ;
118+ }
119+ } catch ( roleError ) {
120+ fastify . log . warn ( roleError , 'Failed to fetch user role for logout event' ) ;
121+ }
122+
123+ const eventContext : EventContext = {
124+ db,
125+ logger : fastify . log ,
126+ user : {
127+ id : request . user . id ,
128+ email : ( request . user as any ) . email ,
129+ roleId : userRole
130+ } ,
131+ request : {
132+ ip : request . ip ,
133+ userAgent : request . headers [ 'user-agent' ] ,
134+ requestId : request . id
135+ } ,
136+ timestamp : new Date ( )
137+ } ;
138+
139+ fastify . eventBus . emitWithContext (
140+ EVENT_NAMES . USER_LOGOUT ,
141+ {
142+ user : {
143+ id : request . user . id ,
144+ email : ( request . user as any ) . email ,
145+ name : ( request . user as any ) . username || `${ ( request . user as any ) . firstName || '' } ${ ( request . user as any ) . lastName || '' } ` . trim ( ) || ( request . user as any ) . email
146+ } ,
147+ metadata : {
148+ ip : request . ip ,
149+ userAgent : request . headers [ 'user-agent' ]
150+ }
151+ } ,
152+ eventContext
153+ ) ;
154+ fastify . log . info ( `USER_LOGOUT event emitted for user: ${ request . user . id } ` ) ;
155+ }
156+ } catch ( eventError ) {
157+ fastify . log . error ( eventError , 'Failed to emit USER_LOGOUT event:' ) ;
158+ // Don't fail logout if event emission fails
159+ }
160+
97161 const response = { success : true , message : 'Logged out successfully.' } ;
98162 const jsonString = JSON . stringify ( response ) ;
99163 return reply . status ( 200 ) . type ( 'application/json' ) . send ( jsonString ) ;
@@ -112,7 +176,7 @@ export default async function logoutRoute(fastify: FastifyInstance) {
112176 if ( authSessionTable && authSessionTable . id ) {
113177 const dbStatus = getDbStatus ( ) ;
114178 if ( dbStatus . dialect === 'sqlite' ) {
115- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179+
116180 const sqliteDb = db as BetterSQLite3Database < any > ;
117181 await sqliteDb . delete ( authSessionTable ) . where ( eq ( authSessionTable . id , sessionId ) ) ;
118182 }
0 commit comments