From dd3b58e061e1e0a3d258d49cdc4c49757d93b25f Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:38:46 -0600 Subject: [PATCH] fix: migrate remaining controllers from Auth0 sub to UUID identity (refs #220) 16 controllers still used request.user.sub (Auth0 ID) instead of request.userContext.userId (UUID) after the user_id column migration, causing 500 errors on all authenticated endpoints including dashboard. Co-Authored-By: Claude Opus 4.6 --- .../src/features/auth/api/auth.controller.ts | 100 ++++++++++-------- .../documents/api/documents.controller.ts | 20 ++-- .../api/email-ingestion.controller.ts | 16 +-- .../fuel-logs/api/fuel-logs.controller.ts | 28 ++--- .../maintenance/api/maintenance.controller.ts | 24 ++--- .../api/notifications.controller.ts | 16 +-- .../onboarding/api/onboarding.controller.ts | 6 +- .../api/ownership-costs.controller.ts | 10 +- .../api/community-stations.controller.ts | 20 ++-- .../stations/api/stations.controller.ts | 20 ++-- .../subscriptions/api/donations.controller.ts | 4 +- .../api/subscriptions.controller.ts | 36 +++---- .../user-export/api/user-export.controller.ts | 2 +- .../user-import/api/user-import.controller.ts | 4 +- .../api/user-preferences.controller.ts | 8 +- .../vehicles/api/vehicles.controller.ts | 32 +++--- 16 files changed, 177 insertions(+), 169 deletions(-) diff --git a/backend/src/features/auth/api/auth.controller.ts b/backend/src/features/auth/api/auth.controller.ts index 2de1a33..67f3788 100644 --- a/backend/src/features/auth/api/auth.controller.ts +++ b/backend/src/features/auth/api/auth.controller.ts @@ -110,17 +110,17 @@ export class AuthController { */ async getVerifyStatus(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const auth0Sub = (request as any).user.sub; - const result = await this.authService.getVerifyStatus(userId); + const result = await this.authService.getVerifyStatus(auth0Sub); - logger.info('Verification status checked', { userId, emailVerified: result.emailVerified }); + logger.info('Verification status checked', { userId: request.userContext?.userId, emailVerified: result.emailVerified }); return reply.code(200).send(result); } catch (error: any) { logger.error('Failed to get verification status', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(500).send({ @@ -137,17 +137,17 @@ export class AuthController { */ async resendVerification(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const auth0Sub = (request as any).user.sub; - const result = await this.authService.resendVerification(userId); + const result = await this.authService.resendVerification(auth0Sub); - logger.info('Verification email resent', { userId }); + logger.info('Verification email resent', { userId: request.userContext?.userId }); return reply.code(200).send(result); } catch (error: any) { logger.error('Failed to resend verification email', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(500).send({ @@ -199,23 +199,26 @@ export class AuthController { */ async getUserStatus(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const auth0Sub = (request as any).user.sub; + const userId = request.userContext?.userId; - const result = await this.authService.getUserStatus(userId); + const result = await this.authService.getUserStatus(auth0Sub); // Log login event to audit trail (called once per Auth0 callback) const ipAddress = this.getClientIp(request); - await auditLogService.info( - 'auth', - userId, - 'User login', - 'user', - userId, - { ipAddress } - ).catch(err => logger.error('Failed to log login audit event', { error: err })); + if (userId) { + await auditLogService.info( + 'auth', + userId, + 'User login', + 'user', + userId, + { ipAddress } + ).catch(err => logger.error('Failed to log login audit event', { error: err })); + } logger.info('User status retrieved', { - userId: userId.substring(0, 8) + '...', + userId: userId?.substring(0, 8) + '...', emailVerified: result.emailVerified, onboardingCompleted: result.onboardingCompleted, }); @@ -224,7 +227,7 @@ export class AuthController { } catch (error: any) { logger.error('Failed to get user status', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(500).send({ @@ -241,12 +244,12 @@ export class AuthController { */ async getSecurityStatus(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const auth0Sub = (request as any).user.sub; - const result = await this.authService.getSecurityStatus(userId); + const result = await this.authService.getSecurityStatus(auth0Sub); logger.info('Security status retrieved', { - userId: userId.substring(0, 8) + '...', + userId: request.userContext?.userId, emailVerified: result.emailVerified, }); @@ -254,7 +257,7 @@ export class AuthController { } catch (error: any) { logger.error('Failed to get security status', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(500).send({ @@ -271,28 +274,31 @@ export class AuthController { */ async requestPasswordReset(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const auth0Sub = (request as any).user.sub; + const userId = request.userContext?.userId; - const result = await this.authService.requestPasswordReset(userId); + const result = await this.authService.requestPasswordReset(auth0Sub); logger.info('Password reset email requested', { - userId: userId.substring(0, 8) + '...', + userId: userId?.substring(0, 8) + '...', }); // Log password reset request to unified audit log - await auditLogService.info( - 'auth', - userId, - 'Password reset requested', - 'user', - userId - ).catch(err => logger.error('Failed to log password reset audit event', { error: err })); + if (userId) { + await auditLogService.info( + 'auth', + userId, + 'Password reset requested', + 'user', + userId + ).catch(err => logger.error('Failed to log password reset audit event', { error: err })); + } return reply.code(200).send(result); } catch (error: any) { logger.error('Failed to request password reset', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(500).send({ @@ -312,21 +318,23 @@ export class AuthController { */ async trackLogout(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext?.userId; const ipAddress = this.getClientIp(request); // Log logout event to audit trail - await auditLogService.info( - 'auth', - userId, - 'User logout', - 'user', - userId, - { ipAddress } - ).catch(err => logger.error('Failed to log logout audit event', { error: err })); + if (userId) { + await auditLogService.info( + 'auth', + userId, + 'User logout', + 'user', + userId, + { ipAddress } + ).catch(err => logger.error('Failed to log logout audit event', { error: err })); + } logger.info('User logout tracked', { - userId: userId.substring(0, 8) + '...', + userId: userId?.substring(0, 8) + '...', }); return reply.code(200).send({ success: true }); @@ -334,7 +342,7 @@ export class AuthController { // Don't block logout on audit failure - always return success logger.error('Failed to track logout', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, }); return reply.code(200).send({ success: true }); diff --git a/backend/src/features/documents/api/documents.controller.ts b/backend/src/features/documents/api/documents.controller.ts index 2de4bdc..c3cc360 100644 --- a/backend/src/features/documents/api/documents.controller.ts +++ b/backend/src/features/documents/api/documents.controller.ts @@ -15,7 +15,7 @@ export class DocumentsController { private readonly service = new DocumentsService(); async list(request: FastifyRequest<{ Querystring: ListQuery }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Documents list requested', { operation: 'documents.list', @@ -43,7 +43,7 @@ export class DocumentsController { } async get(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const documentId = request.params.id; logger.info('Document get requested', { @@ -74,7 +74,7 @@ export class DocumentsController { } async create(request: FastifyRequest<{ Body: CreateBody }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const userTier: SubscriptionTier = request.userContext?.subscriptionTier || 'free'; logger.info('Document create requested', { @@ -120,7 +120,7 @@ export class DocumentsController { } async update(request: FastifyRequest<{ Params: IdParams; Body: UpdateBody }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const userTier: SubscriptionTier = request.userContext?.subscriptionTier || 'free'; const documentId = request.params.id; @@ -174,7 +174,7 @@ export class DocumentsController { } async remove(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const documentId = request.params.id; logger.info('Document delete requested', { @@ -221,7 +221,7 @@ export class DocumentsController { } async upload(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const documentId = request.params.id; logger.info('Document upload requested', { @@ -373,7 +373,7 @@ export class DocumentsController { } async download(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const documentId = request.params.id; logger.info('Document download requested', { @@ -423,7 +423,7 @@ export class DocumentsController { } async listByVehicle(request: FastifyRequest<{ Params: { vehicleId: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const vehicleId = request.params.vehicleId; logger.info('Documents by vehicle requested', { @@ -457,7 +457,7 @@ export class DocumentsController { } async addVehicle(request: FastifyRequest<{ Params: { id: string; vehicleId: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const { id: documentId, vehicleId } = request.params; logger.info('Add vehicle to document requested', { @@ -523,7 +523,7 @@ export class DocumentsController { } async removeVehicle(request: FastifyRequest<{ Params: { id: string; vehicleId: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const { id: documentId, vehicleId } = request.params; logger.info('Remove vehicle from document requested', { diff --git a/backend/src/features/email-ingestion/api/email-ingestion.controller.ts b/backend/src/features/email-ingestion/api/email-ingestion.controller.ts index 5169b37..01dcf09 100644 --- a/backend/src/features/email-ingestion/api/email-ingestion.controller.ts +++ b/backend/src/features/email-ingestion/api/email-ingestion.controller.ts @@ -27,22 +27,22 @@ export class EmailIngestionController { async getPendingAssociations(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const associations = await this.repository.getPendingAssociations(userId); return reply.code(200).send(associations); } catch (error: any) { - logger.error('Error listing pending associations', { error: error.message, userId: (request as any).user?.sub }); + logger.error('Error listing pending associations', { error: error.message, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Failed to list pending associations' }); } } async getPendingAssociationCount(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const count = await this.repository.getPendingAssociationCount(userId); return reply.code(200).send({ count }); } catch (error: any) { - logger.error('Error counting pending associations', { error: error.message, userId: (request as any).user?.sub }); + logger.error('Error counting pending associations', { error: error.message, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Failed to count pending associations' }); } } @@ -52,7 +52,7 @@ export class EmailIngestionController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; const { vehicleId } = request.body; @@ -63,7 +63,7 @@ export class EmailIngestionController { const result = await this.service.resolveAssociation(id, vehicleId, userId); return reply.code(200).send(result); } catch (error: any) { - const userId = (request as any).user?.sub; + const userId = request.userContext?.userId; logger.error('Error resolving pending association', { error: error.message, associationId: request.params.id, @@ -89,13 +89,13 @@ export class EmailIngestionController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; await this.service.dismissAssociation(id, userId); return reply.code(204).send(); } catch (error: any) { - const userId = (request as any).user?.sub; + const userId = request.userContext?.userId; logger.error('Error dismissing pending association', { error: error.message, associationId: request.params.id, diff --git a/backend/src/features/fuel-logs/api/fuel-logs.controller.ts b/backend/src/features/fuel-logs/api/fuel-logs.controller.ts index 370e253..95705fa 100644 --- a/backend/src/features/fuel-logs/api/fuel-logs.controller.ts +++ b/backend/src/features/fuel-logs/api/fuel-logs.controller.ts @@ -20,12 +20,12 @@ export class FuelLogsController { async createFuelLog(request: FastifyRequest<{ Body: EnhancedCreateFuelLogRequest }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const fuelLog = await this.fuelLogsService.createFuelLog(request.body, userId); return reply.code(201).send(fuelLog); } catch (error: any) { - logger.error('Error creating fuel log', { error, userId: (request as any).user?.sub }); + logger.error('Error creating fuel log', { error, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -49,14 +49,14 @@ export class FuelLogsController { async getFuelLogsByVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { vehicleId } = request.params; const fuelLogs = await this.fuelLogsService.getFuelLogsByVehicle(vehicleId, userId); return reply.code(200).send(fuelLogs); } catch (error: any) { - logger.error('Error listing fuel logs', { error, vehicleId: request.params.vehicleId, userId: (request as any).user?.sub }); + logger.error('Error listing fuel logs', { error, vehicleId: request.params.vehicleId, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -80,12 +80,12 @@ export class FuelLogsController { async getUserFuelLogs(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const fuelLogs = await this.fuelLogsService.getUserFuelLogs(userId); return reply.code(200).send(fuelLogs); } catch (error: any) { - logger.error('Error listing all fuel logs', { error, userId: (request as any).user?.sub }); + logger.error('Error listing all fuel logs', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get fuel logs' @@ -95,14 +95,14 @@ export class FuelLogsController { async getFuelLog(request: FastifyRequest<{ Params: FuelLogParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; const fuelLog = await this.fuelLogsService.getFuelLog(id, userId); return reply.code(200).send(fuelLog); } catch (error: any) { - logger.error('Error getting fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error getting fuel log', { error, fuelLogId: request.params.id, userId: request.userContext?.userId }); if (error.message === 'Fuel log not found') { return reply.code(404).send({ @@ -126,14 +126,14 @@ export class FuelLogsController { async updateFuelLog(request: FastifyRequest<{ Params: FuelLogParams; Body: EnhancedUpdateFuelLogRequest }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; const updatedFuelLog = await this.fuelLogsService.updateFuelLog(id, request.body, userId); return reply.code(200).send(updatedFuelLog); } catch (error: any) { - logger.error('Error updating fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error updating fuel log', { error, fuelLogId: request.params.id, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -163,14 +163,14 @@ export class FuelLogsController { async deleteFuelLog(request: FastifyRequest<{ Params: FuelLogParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; await this.fuelLogsService.deleteFuelLog(id, userId); return reply.code(204).send(); } catch (error: any) { - logger.error('Error deleting fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error deleting fuel log', { error, fuelLogId: request.params.id, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -194,14 +194,14 @@ export class FuelLogsController { async getFuelStats(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { vehicleId } = request.params; const stats = await this.fuelLogsService.getVehicleStats(vehicleId, userId); return reply.code(200).send(stats); } catch (error: any) { - logger.error('Error getting fuel stats', { error, vehicleId: request.params.vehicleId, userId: (request as any).user?.sub }); + logger.error('Error getting fuel stats', { error, vehicleId: request.params.vehicleId, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ diff --git a/backend/src/features/maintenance/api/maintenance.controller.ts b/backend/src/features/maintenance/api/maintenance.controller.ts index 815dd1d..5d42970 100644 --- a/backend/src/features/maintenance/api/maintenance.controller.ts +++ b/backend/src/features/maintenance/api/maintenance.controller.ts @@ -18,7 +18,7 @@ export class MaintenanceController { request: FastifyRequest<{ Querystring: { vehicleId?: string; category?: string } }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Maintenance records list requested', { operation: 'maintenance.records.list', @@ -58,7 +58,7 @@ export class MaintenanceController { } async getRecord(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const recordId = request.params.id; logger.info('Maintenance record get requested', { @@ -102,7 +102,7 @@ export class MaintenanceController { request: FastifyRequest<{ Params: { vehicleId: string } }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const vehicleId = request.params.vehicleId; logger.info('Maintenance records by vehicle requested', { @@ -134,7 +134,7 @@ export class MaintenanceController { } async createRecord(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Maintenance record create requested', { operation: 'maintenance.records.create', @@ -190,7 +190,7 @@ export class MaintenanceController { request: FastifyRequest<{ Params: { id: string }; Body: unknown }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const recordId = request.params.id; logger.info('Maintenance record update requested', { @@ -255,7 +255,7 @@ export class MaintenanceController { } async deleteRecord(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const recordId = request.params.id; logger.info('Maintenance record delete requested', { @@ -289,7 +289,7 @@ export class MaintenanceController { request: FastifyRequest<{ Params: { vehicleId: string } }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const vehicleId = request.params.vehicleId; logger.info('Maintenance schedules by vehicle requested', { @@ -321,7 +321,7 @@ export class MaintenanceController { } async createSchedule(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Maintenance schedule create requested', { operation: 'maintenance.schedules.create', @@ -377,7 +377,7 @@ export class MaintenanceController { request: FastifyRequest<{ Params: { id: string }; Body: unknown }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const scheduleId = request.params.id; logger.info('Maintenance schedule update requested', { @@ -442,7 +442,7 @@ export class MaintenanceController { } async deleteSchedule(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const scheduleId = request.params.id; logger.info('Maintenance schedule delete requested', { @@ -476,7 +476,7 @@ export class MaintenanceController { request: FastifyRequest<{ Params: { vehicleId: string }; Querystring: { currentMileage?: string } }>, reply: FastifyReply ) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const vehicleId = request.params.vehicleId; const currentMileage = request.query.currentMileage ? parseInt(request.query.currentMileage, 10) : undefined; @@ -510,7 +510,7 @@ export class MaintenanceController { } async getSubtypes(request: FastifyRequest<{ Params: { category: string } }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const category = request.params.category; logger.info('Maintenance subtypes requested', { diff --git a/backend/src/features/notifications/api/notifications.controller.ts b/backend/src/features/notifications/api/notifications.controller.ts index e9ef66b..e3f147e 100644 --- a/backend/src/features/notifications/api/notifications.controller.ts +++ b/backend/src/features/notifications/api/notifications.controller.ts @@ -24,7 +24,7 @@ export class NotificationsController { // ======================== async getSummary(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user.sub; + const userId = request.userContext!.userId; try { const summary = await this.service.getNotificationSummary(userId); @@ -38,7 +38,7 @@ export class NotificationsController { } async getDueMaintenanceItems(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user.sub; + const userId = request.userContext!.userId; try { const items = await this.service.getDueMaintenanceItems(userId); @@ -52,7 +52,7 @@ export class NotificationsController { } async getExpiringDocuments(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user.sub; + const userId = request.userContext!.userId; try { const documents = await this.service.getExpiringDocuments(userId); @@ -70,7 +70,7 @@ export class NotificationsController { // ======================== async getInAppNotifications(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user!.sub!; + const userId = request.userContext!.userId; const query = request.query as { limit?: string; includeRead?: string }; const limit = query.limit ? parseInt(query.limit, 10) : 20; const includeRead = query.includeRead === 'true'; @@ -85,7 +85,7 @@ export class NotificationsController { } async getUnreadCount(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user!.sub!; + const userId = request.userContext!.userId; try { const count = await this.service.getUnreadCount(userId); @@ -97,7 +97,7 @@ export class NotificationsController { } async markAsRead(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { - const userId = request.user!.sub!; + const userId = request.userContext!.userId; const notificationId = request.params.id; try { @@ -113,7 +113,7 @@ export class NotificationsController { } async markAllAsRead(request: FastifyRequest, reply: FastifyReply) { - const userId = request.user!.sub!; + const userId = request.userContext!.userId; try { const count = await this.service.markAllAsRead(userId); @@ -125,7 +125,7 @@ export class NotificationsController { } async deleteNotification(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { - const userId = request.user!.sub!; + const userId = request.userContext!.userId; const notificationId = request.params.id; try { diff --git a/backend/src/features/onboarding/api/onboarding.controller.ts b/backend/src/features/onboarding/api/onboarding.controller.ts index ce35c50..969bb84 100644 --- a/backend/src/features/onboarding/api/onboarding.controller.ts +++ b/backend/src/features/onboarding/api/onboarding.controller.ts @@ -51,7 +51,7 @@ export class OnboardingController { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error('Error in savePreferences controller', { error, - userId: (request as AuthenticatedRequest).user?.sub, + userId: request.userContext?.userId, }); if (errorMessage === 'User profile not found') { @@ -86,7 +86,7 @@ export class OnboardingController { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error('Error in completeOnboarding controller', { error, - userId: (request as AuthenticatedRequest).user?.sub, + userId: request.userContext?.userId, }); if (errorMessage === 'User profile not found') { @@ -124,7 +124,7 @@ export class OnboardingController { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error('Error in getStatus controller', { error, - userId: (request as AuthenticatedRequest).user?.sub, + userId: request.userContext?.userId, }); if (errorMessage === 'User profile not found') { diff --git a/backend/src/features/ownership-costs/api/ownership-costs.controller.ts b/backend/src/features/ownership-costs/api/ownership-costs.controller.ts index 05f57f9..d92a944 100644 --- a/backend/src/features/ownership-costs/api/ownership-costs.controller.ts +++ b/backend/src/features/ownership-costs/api/ownership-costs.controller.ts @@ -7,7 +7,7 @@ export class OwnershipCostsController { private readonly service = new OwnershipCostsService(); async list(request: FastifyRequest<{ Querystring: ListQuery }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Ownership costs list requested', { operation: 'ownership-costs.list', @@ -35,7 +35,7 @@ export class OwnershipCostsController { } async get(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const costId = request.params.id; logger.info('Ownership cost get requested', { @@ -66,7 +66,7 @@ export class OwnershipCostsController { } async create(request: FastifyRequest<{ Body: CreateBody }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; logger.info('Ownership cost create requested', { operation: 'ownership-costs.create', @@ -91,7 +91,7 @@ export class OwnershipCostsController { } async update(request: FastifyRequest<{ Params: IdParams; Body: UpdateBody }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const costId = request.params.id; logger.info('Ownership cost update requested', { @@ -123,7 +123,7 @@ export class OwnershipCostsController { } async remove(request: FastifyRequest<{ Params: IdParams }>, reply: FastifyReply) { - const userId = (request as any).user?.sub as string; + const userId = request.userContext!.userId; const costId = request.params.id; logger.info('Ownership cost delete requested', { diff --git a/backend/src/features/stations/api/community-stations.controller.ts b/backend/src/features/stations/api/community-stations.controller.ts index fed3da7..477813c 100644 --- a/backend/src/features/stations/api/community-stations.controller.ts +++ b/backend/src/features/stations/api/community-stations.controller.ts @@ -47,7 +47,7 @@ export class CommunityStationsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; // Validate request body const validation = submitCommunityStationSchema.safeParse(request.body); @@ -62,7 +62,7 @@ export class CommunityStationsController { return reply.code(201).send(station); } catch (error: any) { - logger.error('Error submitting station', { error, userId: (request as any).user?.sub }); + logger.error('Error submitting station', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to submit station' @@ -79,7 +79,7 @@ export class CommunityStationsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; // Validate query params const validation = paginationSchema.safeParse(request.query); @@ -94,7 +94,7 @@ export class CommunityStationsController { return reply.code(200).send(result); } catch (error: any) { - logger.error('Error getting user submissions', { error, userId: (request as any).user?.sub }); + logger.error('Error getting user submissions', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to retrieve submissions' @@ -111,7 +111,7 @@ export class CommunityStationsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; // Validate params const validation = stationIdSchema.safeParse(request.params); @@ -128,7 +128,7 @@ export class CommunityStationsController { } catch (error: any) { logger.error('Error withdrawing submission', { error, - userId: (request as any).user?.sub, + userId: request.userContext?.userId, stationId: request.params.id }); @@ -252,7 +252,7 @@ export class CommunityStationsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; // Validate params const paramsValidation = stationIdSchema.safeParse(request.params); @@ -280,7 +280,7 @@ export class CommunityStationsController { return reply.code(200).send(result); } catch (error: any) { - logger.error('Error reporting removal', { error, userId: (request as any).user?.sub }); + logger.error('Error reporting removal', { error, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -379,7 +379,7 @@ export class CommunityStationsController { reply: FastifyReply ): Promise { try { - const adminId = (request as any).user.sub; + const adminId = request.userContext!.userId; // Validate params const paramsValidation = stationIdSchema.safeParse(request.params); @@ -422,7 +422,7 @@ export class CommunityStationsController { return reply.code(200).send(station); } catch (error: any) { - logger.error('Error reviewing station', { error, adminId: (request as any).user?.sub }); + logger.error('Error reviewing station', { error, adminId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ diff --git a/backend/src/features/stations/api/stations.controller.ts b/backend/src/features/stations/api/stations.controller.ts index 271b1f4..2fde67e 100644 --- a/backend/src/features/stations/api/stations.controller.ts +++ b/backend/src/features/stations/api/stations.controller.ts @@ -27,7 +27,7 @@ export class StationsController { async searchStations(request: FastifyRequest<{ Body: StationSearchBody }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { latitude, longitude, radius, fuelType } = request.body; if (!latitude || !longitude) { @@ -46,7 +46,7 @@ export class StationsController { return reply.code(200).send(result); } catch (error: any) { - logger.error('Error searching stations', { error, userId: (request as any).user?.sub }); + logger.error('Error searching stations', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to search stations' @@ -79,7 +79,7 @@ export class StationsController { async saveStation(request: FastifyRequest<{ Body: SaveStationBody }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { placeId, nickname, @@ -106,7 +106,7 @@ export class StationsController { return reply.code(201).send(result); } catch (error: any) { - logger.error('Error saving station', { error, userId: (request as any).user?.sub }); + logger.error('Error saving station', { error, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ @@ -127,7 +127,7 @@ export class StationsController { reply: FastifyReply ) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { placeId } = request.params; const result = await this.stationsService.updateSavedStation(placeId, userId, request.body); @@ -137,7 +137,7 @@ export class StationsController { logger.error('Error updating saved station', { error, placeId: request.params.placeId, - userId: (request as any).user?.sub + userId: request.userContext?.userId }); if (error.message.includes('not found')) { @@ -156,12 +156,12 @@ export class StationsController { async getSavedStations(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const result = await this.stationsService.getUserSavedStations(userId); return reply.code(200).send(result); } catch (error: any) { - logger.error('Error getting saved stations', { error, userId: (request as any).user?.sub }); + logger.error('Error getting saved stations', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get saved stations' @@ -171,14 +171,14 @@ export class StationsController { async removeSavedStation(request: FastifyRequest<{ Params: StationParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { placeId } = request.params; await this.stationsService.removeSavedStation(placeId, userId); return reply.code(204).send(); } catch (error: any) { - logger.error('Error removing saved station', { error, placeId: request.params.placeId, userId: (request as any).user?.sub }); + logger.error('Error removing saved station', { error, placeId: request.params.placeId, userId: request.userContext?.userId }); if (error.message.includes('not found')) { return reply.code(404).send({ diff --git a/backend/src/features/subscriptions/api/donations.controller.ts b/backend/src/features/subscriptions/api/donations.controller.ts index 72f5209..1c70422 100644 --- a/backend/src/features/subscriptions/api/donations.controller.ts +++ b/backend/src/features/subscriptions/api/donations.controller.ts @@ -28,7 +28,7 @@ export class DonationsController { */ async createDonation(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { amount } = request.body as CreateDonationBody; logger.info('Creating donation', { userId, amount }); @@ -63,7 +63,7 @@ export class DonationsController { */ async getDonations(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; logger.info('Getting donations', { userId }); diff --git a/backend/src/features/subscriptions/api/subscriptions.controller.ts b/backend/src/features/subscriptions/api/subscriptions.controller.ts index e4c6f70..543a00f 100644 --- a/backend/src/features/subscriptions/api/subscriptions.controller.ts +++ b/backend/src/features/subscriptions/api/subscriptions.controller.ts @@ -24,7 +24,7 @@ export class SubscriptionsController { */ async getSubscription(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const subscription = await this.service.getSubscription(userId); @@ -39,7 +39,7 @@ export class SubscriptionsController { reply.status(200).send(subscription); } catch (error: any) { logger.error('Failed to get subscription', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -54,14 +54,14 @@ export class SubscriptionsController { */ async checkNeedsVehicleSelection(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const result = await this.service.checkNeedsVehicleSelection(userId); reply.status(200).send(result); } catch (error: any) { logger.error('Failed to check needs vehicle selection', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -85,8 +85,8 @@ export class SubscriptionsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; - const email = (request as any).user.email; + const userId = request.userContext!.userId; + const email = request.userContext!.email || ''; const { tier, billingCycle, paymentMethodId } = request.body; // Validate inputs @@ -141,7 +141,7 @@ export class SubscriptionsController { reply.status(200).send(updatedSubscription); } catch (error: any) { logger.error('Failed to create checkout', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -156,14 +156,14 @@ export class SubscriptionsController { */ async cancelSubscription(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const subscription = await this.service.cancelSubscription(userId); reply.status(200).send(subscription); } catch (error: any) { logger.error('Failed to cancel subscription', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -178,14 +178,14 @@ export class SubscriptionsController { */ async reactivateSubscription(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const subscription = await this.service.reactivateSubscription(userId); reply.status(200).send(subscription); } catch (error: any) { logger.error('Failed to reactivate subscription', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -207,8 +207,8 @@ export class SubscriptionsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; - const email = (request as any).user.email; + const userId = request.userContext!.userId; + const email = request.userContext!.email || ''; const { paymentMethodId } = request.body; // Validate input @@ -228,7 +228,7 @@ export class SubscriptionsController { }); } catch (error: any) { logger.error('Failed to update payment method', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -243,14 +243,14 @@ export class SubscriptionsController { */ async getInvoices(request: FastifyRequest, reply: FastifyReply): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const invoices = await this.service.getInvoices(userId); reply.status(200).send(invoices); } catch (error: any) { logger.error('Failed to get invoices', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ @@ -273,7 +273,7 @@ export class SubscriptionsController { reply: FastifyReply ): Promise { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { targetTier, vehicleIdsToKeep } = request.body; // Validate inputs @@ -311,7 +311,7 @@ export class SubscriptionsController { reply.status(200).send(updatedSubscription); } catch (error: any) { logger.error('Failed to downgrade subscription', { - userId: (request as any).user?.sub, + userId: request.userContext?.userId, error: error.message, }); reply.status(500).send({ diff --git a/backend/src/features/user-export/api/user-export.controller.ts b/backend/src/features/user-export/api/user-export.controller.ts index 1954a25..489a052 100644 --- a/backend/src/features/user-export/api/user-export.controller.ts +++ b/backend/src/features/user-export/api/user-export.controller.ts @@ -15,7 +15,7 @@ export class UserExportController { } async downloadExport(request: FastifyRequest, reply: FastifyReply): Promise { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; logger.info('User export requested', { userId }); diff --git a/backend/src/features/user-import/api/user-import.controller.ts b/backend/src/features/user-import/api/user-import.controller.ts index fbfb929..b44fd6b 100644 --- a/backend/src/features/user-import/api/user-import.controller.ts +++ b/backend/src/features/user-import/api/user-import.controller.ts @@ -24,7 +24,7 @@ export class UserImportController { * Uploads and imports user data archive */ async uploadAndImport(request: FastifyRequest, reply: FastifyReply): Promise { - const userId = request.user?.sub; + const userId = request.userContext?.userId; if (!userId) { return reply.code(401).send({ error: 'Unauthorized' }); } @@ -139,7 +139,7 @@ export class UserImportController { * Generates preview of import data without executing import */ async generatePreview(request: FastifyRequest, reply: FastifyReply): Promise { - const userId = request.user?.sub; + const userId = request.userContext?.userId; if (!userId) { return reply.code(401).send({ error: 'Unauthorized' }); } diff --git a/backend/src/features/user-preferences/api/user-preferences.controller.ts b/backend/src/features/user-preferences/api/user-preferences.controller.ts index 7003b2e..983f7ef 100644 --- a/backend/src/features/user-preferences/api/user-preferences.controller.ts +++ b/backend/src/features/user-preferences/api/user-preferences.controller.ts @@ -18,7 +18,7 @@ export class UserPreferencesController { async getPreferences(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; let preferences = await this.repository.findByUserId(userId); // Create default preferences if none exist @@ -42,7 +42,7 @@ export class UserPreferencesController { updatedAt: preferences.updatedAt, }); } catch (error) { - logger.error('Error getting user preferences', { error, userId: (request as any).user?.sub }); + logger.error('Error getting user preferences', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get preferences', @@ -55,7 +55,7 @@ export class UserPreferencesController { reply: FastifyReply ) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { unitSystem, currencyCode, timeZone, darkMode } = request.body; // Validate unitSystem if provided @@ -115,7 +115,7 @@ export class UserPreferencesController { updatedAt: preferences.updatedAt, }); } catch (error) { - logger.error('Error updating user preferences', { error, userId: (request as any).user?.sub }); + logger.error('Error updating user preferences', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to update preferences', diff --git a/backend/src/features/vehicles/api/vehicles.controller.ts b/backend/src/features/vehicles/api/vehicles.controller.ts index 9b7ee56..475abc6 100644 --- a/backend/src/features/vehicles/api/vehicles.controller.ts +++ b/backend/src/features/vehicles/api/vehicles.controller.ts @@ -27,7 +27,7 @@ export class VehiclesController { async getUserVehicles(request: FastifyRequest, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; // Use tier-aware method to filter out locked vehicles after downgrade const vehiclesWithStatus = await this.vehiclesService.getUserVehiclesWithTierStatus(userId); // Only return active vehicles (filter out locked ones) @@ -37,7 +37,7 @@ export class VehiclesController { return reply.code(200).send(vehicles); } catch (error) { - logger.error('Error getting user vehicles', { error, userId: (request as any).user?.sub }); + logger.error('Error getting user vehicles', { error, userId: request.userContext?.userId }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get vehicles' @@ -65,12 +65,12 @@ export class VehiclesController { } } - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const vehicle = await this.vehiclesService.createVehicle(request.body, userId); return reply.code(201).send(vehicle); } catch (error: any) { - logger.error('Error creating vehicle', { error, userId: (request as any).user?.sub }); + logger.error('Error creating vehicle', { error, userId: request.userContext?.userId }); if (error instanceof VehicleLimitExceededError) { return reply.code(403).send({ @@ -110,7 +110,7 @@ export class VehiclesController { async getVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; // Check tier status - block access to locked vehicles @@ -131,7 +131,7 @@ export class VehiclesController { return reply.code(200).send(vehicle); } catch (error: any) { - logger.error('Error getting vehicle', { error, vehicleId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error getting vehicle', { error, vehicleId: request.params.id, userId: request.userContext?.userId }); if (error.message === 'Vehicle not found' || error.message === 'Unauthorized') { return reply.code(404).send({ @@ -149,14 +149,14 @@ export class VehiclesController { async updateVehicle(request: FastifyRequest<{ Params: VehicleParams; Body: UpdateVehicleBody }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; const vehicle = await this.vehiclesService.updateVehicle(id, request.body, userId); return reply.code(200).send(vehicle); } catch (error: any) { - logger.error('Error updating vehicle', { error, vehicleId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error updating vehicle', { error, vehicleId: request.params.id, userId: request.userContext?.userId }); if (error.message === 'Vehicle not found' || error.message === 'Unauthorized') { return reply.code(404).send({ @@ -183,14 +183,14 @@ export class VehiclesController { async deleteVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; await this.vehiclesService.deleteVehicle(id, userId); return reply.code(204).send(); } catch (error: any) { - logger.error('Error deleting vehicle', { error, vehicleId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error deleting vehicle', { error, vehicleId: request.params.id, userId: request.userContext?.userId }); if (error.message === 'Vehicle not found' || error.message === 'Unauthorized') { return reply.code(404).send({ @@ -208,13 +208,13 @@ export class VehiclesController { async getTCO(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const { id } = request.params; const tco = await this.vehiclesService.getTCO(id, userId); return reply.code(200).send(tco); } catch (error: any) { - logger.error('Error getting vehicle TCO', { error, vehicleId: request.params.id, userId: (request as any).user?.sub }); + logger.error('Error getting vehicle TCO', { error, vehicleId: request.params.id, userId: request.userContext?.userId }); if (error.statusCode === 404 || error.message === 'Vehicle not found') { return reply.code(404).send({ @@ -383,7 +383,7 @@ export class VehiclesController { * Requires Pro or Enterprise tier */ async decodeVin(request: FastifyRequest<{ Body: DecodeVinRequest }>, reply: FastifyReply) { - const userId = (request as any).user?.sub; + const userId = request.userContext?.userId; try { const { vin } = request.body; @@ -447,7 +447,7 @@ export class VehiclesController { } async uploadImage(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const vehicleId = request.params.id; logger.info('Vehicle image upload requested', { @@ -604,7 +604,7 @@ export class VehiclesController { } async downloadImage(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const vehicleId = request.params.id; logger.info('Vehicle image download requested', { @@ -654,7 +654,7 @@ export class VehiclesController { } async deleteImage(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { - const userId = (request as any).user.sub; + const userId = request.userContext!.userId; const vehicleId = request.params.id; logger.info('Vehicle image delete requested', {