Modernization Project Complete. Updated to latest versions of frameworks.
This commit is contained in:
@@ -1,186 +1,219 @@
|
||||
/**
|
||||
* @ai-summary HTTP request handlers for fuel logs
|
||||
* @ai-summary Fastify route handlers for fuel logs API
|
||||
* @ai-context HTTP request/response handling with Fastify reply methods
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { FuelLogsService } from '../domain/fuel-logs.service';
|
||||
import { validateCreateFuelLog, validateUpdateFuelLog } from './fuel-logs.validators';
|
||||
import { FuelLogsRepository } from '../data/fuel-logs.repository';
|
||||
import { pool } from '../../../core/config/database';
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
import { CreateFuelLogBody, UpdateFuelLogBody, FuelLogParams, VehicleParams } from '../domain/fuel-logs.types';
|
||||
|
||||
export class FuelLogsController {
|
||||
constructor(private service: FuelLogsService) {}
|
||||
private fuelLogsService: FuelLogsService;
|
||||
|
||||
constructor() {
|
||||
const repository = new FuelLogsRepository(pool);
|
||||
this.fuelLogsService = new FuelLogsService(repository);
|
||||
}
|
||||
|
||||
create = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async createFuelLog(request: FastifyRequest<{ Body: CreateFuelLogBody }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = (request as any).user.sub;
|
||||
const fuelLog = await this.fuelLogsService.createFuelLog(request.body, userId);
|
||||
|
||||
const validation = validateCreateFuelLog(req.body);
|
||||
if (!validation.success) {
|
||||
return res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: validation.error.errors
|
||||
return reply.code(201).send(fuelLog);
|
||||
} catch (error: any) {
|
||||
logger.error('Error creating fuel log', { error, userId: (request as any).user?.sub });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message.includes('Unauthorized')) {
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
const result = await this.service.createFuelLog(validation.data, userId);
|
||||
res.status(201).json(result);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to create fuel log'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getFuelLogsByVehicle(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = (request as any).user.sub;
|
||||
const { vehicleId } = request.params;
|
||||
|
||||
const fuelLogs = await this.fuelLogsService.getFuelLogsByVehicle(vehicleId, userId);
|
||||
|
||||
return reply.code(200).send(fuelLogs);
|
||||
} catch (error: any) {
|
||||
logger.error('Error creating fuel log', { error: error.message });
|
||||
logger.error('Error listing fuel logs', { error, vehicleId: request.params.vehicleId, userId: (request as any).user?.sub });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return res.status(404).json({ error: error.message });
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message.includes('Unauthorized')) {
|
||||
return res.status(403).json({ error: error.message });
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to get fuel logs'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
listByVehicle = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async getUserFuelLogs(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = (request as any).user.sub;
|
||||
const fuelLogs = await this.fuelLogsService.getUserFuelLogs(userId);
|
||||
|
||||
const { vehicleId } = req.params;
|
||||
const result = await this.service.getFuelLogsByVehicle(vehicleId, userId);
|
||||
res.json(result);
|
||||
return reply.code(200).send(fuelLogs);
|
||||
} catch (error: any) {
|
||||
logger.error('Error listing fuel logs', { error: error.message });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return res.status(404).json({ error: error.message });
|
||||
}
|
||||
if (error.message.includes('Unauthorized')) {
|
||||
return res.status(403).json({ error: error.message });
|
||||
}
|
||||
|
||||
return next(error);
|
||||
logger.error('Error listing all fuel logs', { error, userId: (request as any).user?.sub });
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to get fuel logs'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
listAll = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async getFuelLog(request: FastifyRequest<{ Params: FuelLogParams }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = (request as any).user.sub;
|
||||
const { id } = request.params;
|
||||
|
||||
const result = await this.service.getUserFuelLogs(userId);
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
logger.error('Error listing all fuel logs', { error: error.message });
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
get = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const fuelLog = await this.fuelLogsService.getFuelLog(id, userId);
|
||||
|
||||
const { id } = req.params;
|
||||
const result = await this.service.getFuelLog(id, userId);
|
||||
res.json(result);
|
||||
return reply.code(200).send(fuelLog);
|
||||
} catch (error: any) {
|
||||
logger.error('Error getting fuel log', { error: error.message });
|
||||
logger.error('Error getting fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub });
|
||||
|
||||
if (error.message === 'Fuel log not found') {
|
||||
return res.status(404).json({ error: error.message });
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message === 'Unauthorized') {
|
||||
return res.status(403).json({ error: error.message });
|
||||
}
|
||||
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
update = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
const validation = validateUpdateFuelLog(req.body);
|
||||
if (!validation.success) {
|
||||
return res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: validation.error.errors
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
const result = await this.service.updateFuelLog(id, validation.data, userId);
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
logger.error('Error updating fuel log', { error: error.message });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return res.status(404).json({ error: error.message });
|
||||
}
|
||||
if (error.message === 'Unauthorized') {
|
||||
return res.status(403).json({ error: error.message });
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to get fuel log'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delete = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async updateFuelLog(request: FastifyRequest<{ Params: FuelLogParams; Body: UpdateFuelLogBody }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = (request as any).user.sub;
|
||||
const { id } = request.params;
|
||||
|
||||
const { id } = req.params;
|
||||
await this.service.deleteFuelLog(id, userId);
|
||||
res.status(204).send();
|
||||
const fuelLog = await this.fuelLogsService.updateFuelLog(id, request.body, userId);
|
||||
|
||||
return reply.code(200).send(fuelLog);
|
||||
} catch (error: any) {
|
||||
logger.error('Error deleting fuel log', { error: error.message });
|
||||
logger.error('Error updating fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return res.status(404).json({ error: error.message });
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message === 'Unauthorized') {
|
||||
return res.status(403).json({ error: error.message });
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to update fuel log'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getStats = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async deleteFuelLog(request: FastifyRequest<{ Params: FuelLogParams }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = (request as any).user.sub;
|
||||
const { id } = request.params;
|
||||
|
||||
const { vehicleId } = req.params;
|
||||
const result = await this.service.getVehicleStats(vehicleId, userId);
|
||||
res.json(result);
|
||||
await this.fuelLogsService.deleteFuelLog(id, userId);
|
||||
|
||||
return reply.code(204).send();
|
||||
} catch (error: any) {
|
||||
logger.error('Error getting fuel stats', { error: error.message });
|
||||
logger.error('Error deleting fuel log', { error, fuelLogId: request.params.id, userId: (request as any).user?.sub });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return res.status(404).json({ error: error.message });
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message === 'Unauthorized') {
|
||||
return res.status(403).json({ error: error.message });
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to delete fuel log'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getFuelStats(request: FastifyRequest<{ Params: VehicleParams }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = (request as any).user.sub;
|
||||
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 });
|
||||
|
||||
if (error.message.includes('not found')) {
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
if (error.message === 'Unauthorized') {
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to get fuel stats'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,68 @@
|
||||
/**
|
||||
* @ai-summary Route definitions for fuel logs API
|
||||
* @ai-summary Fastify routes for fuel logs API
|
||||
* @ai-context Route definitions with Fastify plugin pattern and authentication
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import {
|
||||
CreateFuelLogBody,
|
||||
UpdateFuelLogBody,
|
||||
FuelLogParams,
|
||||
VehicleParams
|
||||
} from '../domain/fuel-logs.types';
|
||||
import { FuelLogsController } from './fuel-logs.controller';
|
||||
import { FuelLogsService } from '../domain/fuel-logs.service';
|
||||
import { FuelLogsRepository } from '../data/fuel-logs.repository';
|
||||
import { authMiddleware } from '../../../core/security/auth.middleware';
|
||||
import pool from '../../../core/config/database';
|
||||
|
||||
export function registerFuelLogsRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
// Initialize layers
|
||||
const repository = new FuelLogsRepository(pool);
|
||||
const service = new FuelLogsService(repository);
|
||||
const controller = new FuelLogsController(service);
|
||||
|
||||
// Define routes
|
||||
router.get('/api/fuel-logs', authMiddleware, controller.listAll);
|
||||
router.get('/api/fuel-logs/:id', authMiddleware, controller.get);
|
||||
router.post('/api/fuel-logs', authMiddleware, controller.create);
|
||||
router.put('/api/fuel-logs/:id', authMiddleware, controller.update);
|
||||
router.delete('/api/fuel-logs/:id', authMiddleware, controller.delete);
|
||||
|
||||
// Vehicle-specific routes
|
||||
router.get('/api/vehicles/:vehicleId/fuel-logs', authMiddleware, controller.listByVehicle);
|
||||
router.get('/api/vehicles/:vehicleId/fuel-stats', authMiddleware, controller.getStats);
|
||||
|
||||
return router;
|
||||
export const fuelLogsRoutes: FastifyPluginAsync = async (
|
||||
fastify: FastifyInstance,
|
||||
_opts: FastifyPluginOptions
|
||||
) => {
|
||||
const fuelLogsController = new FuelLogsController();
|
||||
|
||||
// GET /api/fuel-logs - Get user's fuel logs
|
||||
fastify.get('/fuel-logs', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.getUserFuelLogs.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// POST /api/fuel-logs - Create new fuel log
|
||||
fastify.post<{ Body: CreateFuelLogBody }>('/fuel-logs', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.createFuelLog.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// GET /api/fuel-logs/:id - Get specific fuel log
|
||||
fastify.get<{ Params: FuelLogParams }>('/fuel-logs/:id', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.getFuelLog.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// PUT /api/fuel-logs/:id - Update fuel log
|
||||
fastify.put<{ Params: FuelLogParams; Body: UpdateFuelLogBody }>('/fuel-logs/:id', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.updateFuelLog.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// DELETE /api/fuel-logs/:id - Delete fuel log
|
||||
fastify.delete<{ Params: FuelLogParams }>('/fuel-logs/:id', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.deleteFuelLog.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// GET /api/vehicles/:vehicleId/fuel-logs - Get fuel logs for specific vehicle
|
||||
fastify.get<{ Params: VehicleParams }>('/vehicles/:vehicleId/fuel-logs', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.getFuelLogsByVehicle.bind(fuelLogsController)
|
||||
});
|
||||
|
||||
// GET /api/vehicles/:vehicleId/fuel-stats - Get fuel stats for specific vehicle
|
||||
fastify.get<{ Params: VehicleParams }>('/vehicles/:vehicleId/fuel-stats', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: fuelLogsController.getFuelStats.bind(fuelLogsController)
|
||||
});
|
||||
};
|
||||
|
||||
// For backward compatibility during migration
|
||||
export function registerFuelLogsRoutes() {
|
||||
throw new Error('registerFuelLogsRoutes is deprecated - use fuelLogsRoutes Fastify plugin instead');
|
||||
}
|
||||
@@ -67,4 +67,36 @@ export interface FuelStats {
|
||||
averageMPG: number;
|
||||
totalMiles: number;
|
||||
logCount: number;
|
||||
}
|
||||
|
||||
// Fastify-specific types for HTTP handling
|
||||
export interface CreateFuelLogBody {
|
||||
vehicleId: string;
|
||||
date: string;
|
||||
odometer: number;
|
||||
gallons: number;
|
||||
pricePerGallon: number;
|
||||
totalCost: number;
|
||||
station?: string;
|
||||
location?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface UpdateFuelLogBody {
|
||||
date?: string;
|
||||
odometer?: number;
|
||||
gallons?: number;
|
||||
pricePerGallon?: number;
|
||||
totalCost?: number;
|
||||
station?: string;
|
||||
location?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface FuelLogParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface VehicleParams {
|
||||
vehicleId: string;
|
||||
}
|
||||
@@ -14,5 +14,5 @@ export type {
|
||||
FuelStats
|
||||
} from './domain/fuel-logs.types';
|
||||
|
||||
// Internal: Register routes
|
||||
export { registerFuelLogsRoutes } from './api/fuel-logs.routes';
|
||||
// Internal: Register routes with Fastify app
|
||||
export { fuelLogsRoutes, registerFuelLogsRoutes } from './api/fuel-logs.routes';
|
||||
|
||||
Reference in New Issue
Block a user