Initial Commit

This commit is contained in:
Eric Gullickson
2025-09-17 16:09:15 -05:00
parent 0cdb9803de
commit a052040e3a
373 changed files with 437090 additions and 6773 deletions

View File

@@ -35,6 +35,18 @@ export class VehiclesController {
async createVehicle(request: FastifyRequest<{ Body: CreateVehicleBody }>, reply: FastifyReply) {
try {
// Require either a valid 17-char VIN or a non-empty license plate
const vin = request.body?.vin?.trim();
const plate = request.body?.licensePlate?.trim();
const hasValidVin = !!vin && vin.length === 17;
const hasPlate = !!plate && plate.length > 0;
if (!hasValidVin && !hasPlate) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Either a valid 17-character VIN or a license plate is required'
});
}
const userId = (request as any).user.sub;
const vehicle = await this.vehiclesService.createVehicle(request.body, userId);
@@ -138,12 +150,20 @@ export class VehiclesController {
}
}
async getDropdownMakes(_request: FastifyRequest, reply: FastifyReply) {
async getDropdownMakes(request: FastifyRequest<{ Querystring: { year: number } }>, reply: FastifyReply) {
try {
const makes = await this.vehiclesService.getDropdownMakes();
const { year } = request.query;
if (!year || year < 1980 || year > new Date().getFullYear() + 1) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Valid year parameter is required (1980-' + (new Date().getFullYear() + 1) + ')'
});
}
const makes = await this.vehiclesService.getDropdownMakes(year);
return reply.code(200).send(makes);
} catch (error) {
logger.error('Error getting dropdown makes', { error });
logger.error('Error getting dropdown makes', { error, year: request.query?.year });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get makes'
@@ -151,13 +171,20 @@ export class VehiclesController {
}
}
async getDropdownModels(request: FastifyRequest<{ Params: { make: string } }>, reply: FastifyReply) {
async getDropdownModels(request: FastifyRequest<{ Querystring: { year: number; make_id: number } }>, reply: FastifyReply) {
try {
const { make } = request.params;
const models = await this.vehiclesService.getDropdownModels(make);
const { year, make_id } = request.query;
if (!year || !make_id || year < 1980 || year > new Date().getFullYear() + 1 || make_id < 1) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Valid year and make_id parameters are required'
});
}
const models = await this.vehiclesService.getDropdownModels(year, make_id);
return reply.code(200).send(models);
} catch (error) {
logger.error('Error getting dropdown models', { error, make: request.params.make });
logger.error('Error getting dropdown models', { error, year: request.query?.year, make_id: request.query?.make_id });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get models'
@@ -165,12 +192,20 @@ export class VehiclesController {
}
}
async getDropdownTransmissions(_request: FastifyRequest, reply: FastifyReply) {
async getDropdownTransmissions(request: FastifyRequest<{ Querystring: { year: number; make_id: number; model_id: number } }>, reply: FastifyReply) {
try {
const transmissions = await this.vehiclesService.getDropdownTransmissions();
const { year, make_id, model_id } = request.query;
if (!year || !make_id || !model_id || year < 1980 || year > new Date().getFullYear() + 1 || make_id < 1 || model_id < 1) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Valid year, make_id, and model_id parameters are required'
});
}
const transmissions = await this.vehiclesService.getDropdownTransmissions(year, make_id, model_id);
return reply.code(200).send(transmissions);
} catch (error) {
logger.error('Error getting dropdown transmissions', { error });
logger.error('Error getting dropdown transmissions', { error, year: request.query?.year, make_id: request.query?.make_id, model_id: request.query?.model_id });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get transmissions'
@@ -178,12 +213,20 @@ export class VehiclesController {
}
}
async getDropdownEngines(_request: FastifyRequest, reply: FastifyReply) {
async getDropdownEngines(request: FastifyRequest<{ Querystring: { year: number; make_id: number; model_id: number; trim_id: number } }>, reply: FastifyReply) {
try {
const engines = await this.vehiclesService.getDropdownEngines();
const { year, make_id, model_id, trim_id } = request.query;
if (!year || !make_id || !model_id || !trim_id || year < 1980 || year > new Date().getFullYear() + 1 || make_id < 1 || model_id < 1 || trim_id < 1) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Valid year, make_id, model_id, and trim_id parameters are required'
});
}
const engines = await this.vehiclesService.getDropdownEngines(year, make_id, model_id, trim_id);
return reply.code(200).send(engines);
} catch (error) {
logger.error('Error getting dropdown engines', { error });
logger.error('Error getting dropdown engines', { error, year: request.query?.year, make_id: request.query?.make_id, model_id: request.query?.model_id, trim_id: request.query?.trim_id });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get engines'
@@ -191,16 +234,62 @@ export class VehiclesController {
}
}
async getDropdownTrims(_request: FastifyRequest, reply: FastifyReply) {
async getDropdownTrims(request: FastifyRequest<{ Querystring: { year: number; make_id: number; model_id: number } }>, reply: FastifyReply) {
try {
const trims = await this.vehiclesService.getDropdownTrims();
const { year, make_id, model_id } = request.query;
if (!year || !make_id || !model_id || year < 1980 || year > new Date().getFullYear() + 1 || make_id < 1 || model_id < 1) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Valid year, make_id, and model_id parameters are required'
});
}
const trims = await this.vehiclesService.getDropdownTrims(year, make_id, model_id);
return reply.code(200).send(trims);
} catch (error) {
logger.error('Error getting dropdown trims', { error });
logger.error('Error getting dropdown trims', { error, year: request.query?.year, make_id: request.query?.make_id, model_id: request.query?.model_id });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get trims'
});
}
}
}
async getDropdownYears(_request: FastifyRequest, reply: FastifyReply) {
try {
// Use platform client through VehiclesService's integration
const years = await this.vehiclesService.getDropdownYears();
return reply.code(200).send(years);
} catch (error) {
logger.error('Error getting dropdown years', { error });
return reply.code(500).send({
error: 'Internal server error',
message: 'Failed to get years'
});
}
}
async decodeVIN(request: FastifyRequest<{ Body: { vin: string } }>, reply: FastifyReply) {
try {
const { vin } = request.body;
if (!vin || vin.length !== 17) {
return reply.code(400).send({
vin: vin || '',
success: false,
error: 'VIN must be exactly 17 characters'
});
}
const result = await this.vehiclesService.decodeVIN(vin);
return reply.code(200).send(result);
} catch (error: any) {
logger.error('Error decoding VIN', { error, vin: request.body?.vin });
return reply.code(500).send({
vin: request.body?.vin || '',
success: false,
error: 'VIN decode failed'
});
}
}
}