/** * @ai-summary Fastify route handlers for vehicles API * @ai-context HTTP request/response handling with Fastify reply methods */ import { FastifyRequest, FastifyReply } from 'fastify'; import { VehiclesService } from '../domain/vehicles.service'; import { VehiclesRepository } from '../data/vehicles.repository'; import { pool } from '../../../core/config/database'; import { logger } from '../../../core/logging/logger'; import { CreateVehicleBody, UpdateVehicleBody, VehicleParams } from '../domain/vehicles.types'; export class VehiclesController { private vehiclesService: VehiclesService; constructor() { const repository = new VehiclesRepository(pool); this.vehiclesService = new VehiclesService(repository); } async getUserVehicles(request: FastifyRequest, reply: FastifyReply) { try { const userId = (request as any).user.sub; const vehicles = await this.vehiclesService.getUserVehicles(userId); return reply.code(200).send(vehicles); } catch (error) { logger.error('Error getting user vehicles', { error, userId: (request as any).user?.sub }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get vehicles' }); } } async createVehicle(request: FastifyRequest<{ Body: CreateVehicleBody }>, reply: FastifyReply) { try { const userId = (request as any).user.sub; 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 }); if (error.message === 'Invalid VIN format') { return reply.code(400).send({ error: 'Bad Request', message: error.message }); } if (error.message === 'Vehicle with this VIN already exists') { return reply.code(400).send({ error: 'Bad Request', message: error.message }); } return reply.code(500).send({ error: 'Internal server error', message: 'Failed to create vehicle' }); } } async getVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { const userId = (request as any).user.sub; const { id } = request.params; 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' }); } } async updateVehicle(request: FastifyRequest<{ Params: VehicleParams; Body: UpdateVehicleBody }>, reply: FastifyReply) { try { const userId = (request as any).user.sub; 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 }); 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 update vehicle' }); } } async deleteVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) { try { const userId = (request as any).user.sub; 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 }); 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 delete vehicle' }); } } async getDropdownMakes(_request: FastifyRequest, reply: FastifyReply) { try { const makes = await this.vehiclesService.getDropdownMakes(); return reply.code(200).send(makes); } catch (error) { logger.error('Error getting dropdown makes', { error }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get makes' }); } } async getDropdownModels(request: FastifyRequest<{ Params: { make: string } }>, reply: FastifyReply) { try { const { make } = request.params; const models = await this.vehiclesService.getDropdownModels(make); return reply.code(200).send(models); } catch (error) { logger.error('Error getting dropdown models', { error, make: request.params.make }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get models' }); } } async getDropdownTransmissions(_request: FastifyRequest, reply: FastifyReply) { try { const transmissions = await this.vehiclesService.getDropdownTransmissions(); return reply.code(200).send(transmissions); } catch (error) { logger.error('Error getting dropdown transmissions', { error }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get transmissions' }); } } async getDropdownEngines(_request: FastifyRequest, reply: FastifyReply) { try { const engines = await this.vehiclesService.getDropdownEngines(); return reply.code(200).send(engines); } catch (error) { logger.error('Error getting dropdown engines', { error }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get engines' }); } } async getDropdownTrims(_request: FastifyRequest, reply: FastifyReply) { try { const trims = await this.vehiclesService.getDropdownTrims(); return reply.code(200).send(trims); } catch (error) { logger.error('Error getting dropdown trims', { error }); return reply.code(500).send({ error: 'Internal server error', message: 'Failed to get trims' }); } } }