MVP Build

This commit is contained in:
Eric Gullickson
2025-08-09 12:47:15 -05:00
parent 2e8816df7f
commit 8f5117a4e2
92 changed files with 5910 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
/**
* @ai-summary HTTP request handlers for vehicles API
* @ai-context Handles validation, auth, and delegates to service layer
*/
import { Request, Response, NextFunction } from 'express';
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 { ZodError } from 'zod';
import {
createVehicleSchema,
updateVehicleSchema,
vehicleIdSchema,
CreateVehicleInput,
UpdateVehicleInput,
} from './vehicles.validation';
export class VehiclesController {
private service: VehiclesService;
constructor() {
const repository = new VehiclesRepository(pool);
this.service = new VehiclesService(repository);
}
createVehicle = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
// Validate request body
const data = createVehicleSchema.parse(req.body) as CreateVehicleInput;
// Get user ID from JWT token
const userId = req.user?.sub;
if (!userId) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const vehicle = await this.service.createVehicle(data, userId);
logger.info('Vehicle created successfully', { vehicleId: vehicle.id, userId });
res.status(201).json(vehicle);
} catch (error: any) {
if (error instanceof ZodError) {
res.status(400).json({ error: error.errors[0].message });
return;
}
if (error.message === 'Invalid VIN format' ||
error.message === 'Vehicle with this VIN already exists') {
res.status(400).json({ error: error.message });
return;
}
next(error);
}
};
getUserVehicles = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const userId = req.user?.sub;
if (!userId) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const vehicles = await this.service.getUserVehicles(userId);
res.json(vehicles);
} catch (error) {
next(error);
}
};
getVehicle = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const { id } = vehicleIdSchema.parse(req.params);
const userId = req.user?.sub;
if (!userId) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const vehicle = await this.service.getVehicle(id, userId);
res.json(vehicle);
} catch (error: any) {
if (error instanceof ZodError) {
res.status(400).json({ error: error.errors[0].message });
return;
}
if (error.message === 'Vehicle not found') {
res.status(404).json({ error: 'Vehicle not found' });
return;
}
if (error.message === 'Unauthorized') {
res.status(403).json({ error: 'Access denied' });
return;
}
next(error);
}
};
updateVehicle = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const { id } = vehicleIdSchema.parse(req.params);
const data = updateVehicleSchema.parse(req.body) as UpdateVehicleInput;
const userId = req.user?.sub;
if (!userId) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const vehicle = await this.service.updateVehicle(id, data, userId);
logger.info('Vehicle updated successfully', { vehicleId: id, userId });
res.json(vehicle);
} catch (error: any) {
if (error instanceof ZodError) {
res.status(400).json({ error: error.errors[0].message });
return;
}
if (error.message === 'Vehicle not found') {
res.status(404).json({ error: 'Vehicle not found' });
return;
}
if (error.message === 'Unauthorized') {
res.status(403).json({ error: 'Access denied' });
return;
}
next(error);
}
};
deleteVehicle = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const { id } = vehicleIdSchema.parse(req.params);
const userId = req.user?.sub;
if (!userId) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
await this.service.deleteVehicle(id, userId);
logger.info('Vehicle deleted successfully', { vehicleId: id, userId });
res.status(204).send();
} catch (error: any) {
if (error instanceof ZodError) {
res.status(400).json({ error: error.errors[0].message });
return;
}
if (error.message === 'Vehicle not found') {
res.status(404).json({ error: 'Vehicle not found' });
return;
}
if (error.message === 'Unauthorized') {
res.status(403).json({ error: 'Access denied' });
return;
}
next(error);
}
};
}