From 68948484a4aebf7e369cf2370fe0aa279ca3ad8e Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Sat, 24 Jan 2026 11:51:36 -0600 Subject: [PATCH] fix: filter locked vehicles after tier downgrade selection (refs #60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /api/vehicles now uses getUserVehiclesWithTierStatus() and filters out vehicles with tierStatus='locked' so only selected vehicles appear in the vehicle list - GET /api/vehicles/:id now checks tier status and returns 403 TIER_REQUIRED if user tries to access a locked vehicle directly This ensures that after a user selects 2 vehicles during downgrade to free tier, only those 2 vehicles appear in the summary and details screens. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../vehicles/api/vehicles.controller.ts | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/backend/src/features/vehicles/api/vehicles.controller.ts b/backend/src/features/vehicles/api/vehicles.controller.ts index 4bf7870..9b7ee56 100644 --- a/backend/src/features/vehicles/api/vehicles.controller.ts +++ b/backend/src/features/vehicles/api/vehicles.controller.ts @@ -28,8 +28,13 @@ export class VehiclesController { async getUserVehicles(request: FastifyRequest, reply: FastifyReply) { try { const userId = (request as any).user.sub; - const vehicles = await this.vehiclesService.getUserVehicles(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) + const vehicles = vehiclesWithStatus + .filter(v => v.tierStatus === 'active') + .map(({ tierStatus, ...vehicle }) => vehicle); + return reply.code(200).send(vehicles); } catch (error) { logger.error('Error getting user vehicles', { error, userId: (request as any).user?.sub }); @@ -107,20 +112,34 @@ export class VehiclesController { try { const userId = (request as any).user.sub; const { id } = request.params; - + + // Check tier status - block access to locked vehicles + const vehiclesWithStatus = await this.vehiclesService.getUserVehiclesWithTierStatus(userId); + const vehicleStatus = vehiclesWithStatus.find(v => v.id === id); + if (vehicleStatus && vehicleStatus.tierStatus === 'locked') { + return reply.code(403).send({ + error: 'TIER_REQUIRED', + requiredTier: 'pro', + feature: 'vehicle.access', + featureName: 'Vehicle Access', + upgradePrompt: 'Upgrade to Pro to access all your vehicles', + message: 'This vehicle is not available on your current subscription tier' + }); + } + const vehicle = await this.vehiclesService.getVehicle(id, userId); - + 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 }); - + if (error.message === 'Vehicle not found' || error.message === 'Unauthorized') { return reply.code(404).send({ error: 'Not Found', message: 'Vehicle not found' }); } - + return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get vehicle'