Modernization Project Complete. Updated to latest versions of frameworks.
This commit is contained in:
@@ -1,105 +1,125 @@
|
||||
/**
|
||||
* @ai-summary HTTP request handlers for stations
|
||||
* @ai-summary Fastify route handlers for stations API
|
||||
* @ai-context HTTP request/response handling with Fastify reply methods
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { StationsService } from '../domain/stations.service';
|
||||
import { StationsRepository } from '../data/stations.repository';
|
||||
import { pool } from '../../../core/config/database';
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
import { StationSearchBody, SaveStationBody, StationParams } from '../domain/stations.types';
|
||||
|
||||
export class StationsController {
|
||||
constructor(private service: StationsService) {}
|
||||
private stationsService: StationsService;
|
||||
|
||||
constructor() {
|
||||
const repository = new StationsRepository(pool);
|
||||
this.stationsService = new StationsService(repository);
|
||||
}
|
||||
|
||||
search = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async searchStations(request: FastifyRequest<{ Body: StationSearchBody }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { latitude, longitude, radius, fuelType } = req.body;
|
||||
const userId = (request as any).user.sub;
|
||||
const { latitude, longitude, radius, fuelType } = request.body;
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
return res.status(400).json({ error: 'Latitude and longitude are required' });
|
||||
return reply.code(400).send({
|
||||
error: 'Bad Request',
|
||||
message: 'Latitude and longitude are required'
|
||||
});
|
||||
}
|
||||
|
||||
const result = await this.service.searchNearbyStations({
|
||||
const result = await this.stationsService.searchNearbyStations({
|
||||
latitude,
|
||||
longitude,
|
||||
radius,
|
||||
fuelType
|
||||
}, userId);
|
||||
|
||||
res.json(result);
|
||||
return reply.code(200).send(result);
|
||||
} catch (error: any) {
|
||||
logger.error('Error searching stations', { error: error.message });
|
||||
return next(error);
|
||||
logger.error('Error searching stations', { error, userId: (request as any).user?.sub });
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to search stations'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
save = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async saveStation(request: FastifyRequest<{ Body: SaveStationBody }>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = req.user?.sub;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { placeId, nickname, notes, isFavorite } = req.body;
|
||||
const userId = (request as any).user.sub;
|
||||
const { placeId, nickname, notes, isFavorite } = request.body;
|
||||
|
||||
if (!placeId) {
|
||||
return res.status(400).json({ error: 'Place ID is required' });
|
||||
return reply.code(400).send({
|
||||
error: 'Bad Request',
|
||||
message: 'Place ID is required'
|
||||
});
|
||||
}
|
||||
|
||||
const result = await this.service.saveStation(placeId, userId, {
|
||||
const result = await this.stationsService.saveStation(placeId, userId, {
|
||||
nickname,
|
||||
notes,
|
||||
isFavorite
|
||||
});
|
||||
|
||||
res.status(201).json(result);
|
||||
return reply.code(201).send(result);
|
||||
} catch (error: any) {
|
||||
logger.error('Error saving station', { error: error.message });
|
||||
logger.error('Error saving station', { error, 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
|
||||
});
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to save station'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getSaved = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async getSavedStations(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 result = await this.stationsService.getUserSavedStations(userId);
|
||||
|
||||
const result = await this.service.getUserSavedStations(userId);
|
||||
res.json(result);
|
||||
return reply.code(200).send(result);
|
||||
} catch (error: any) {
|
||||
logger.error('Error getting saved stations', { error: error.message });
|
||||
return next(error);
|
||||
logger.error('Error getting saved stations', { error, userId: (request as any).user?.sub });
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to get saved stations'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
removeSaved = async (req: Request, res: Response, next: NextFunction) => {
|
||||
async removeSavedStation(request: FastifyRequest<{ Params: StationParams }>, 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 { placeId } = request.params;
|
||||
|
||||
const { placeId } = req.params;
|
||||
await this.service.removeSavedStation(placeId, userId);
|
||||
res.status(204).send();
|
||||
await this.stationsService.removeSavedStation(placeId, userId);
|
||||
|
||||
return reply.code(204).send();
|
||||
} catch (error: any) {
|
||||
logger.error('Error removing saved station', { error: error.message });
|
||||
logger.error('Error removing saved station', { error, placeId: request.params.placeId, 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
|
||||
});
|
||||
}
|
||||
|
||||
return next(error);
|
||||
return reply.code(500).send({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to remove saved station'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,49 @@
|
||||
/**
|
||||
* @ai-summary Route definitions for stations API
|
||||
* @ai-summary Fastify routes for stations 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 {
|
||||
StationSearchBody,
|
||||
SaveStationBody,
|
||||
StationParams
|
||||
} from '../domain/stations.types';
|
||||
import { StationsController } from './stations.controller';
|
||||
import { StationsService } from '../domain/stations.service';
|
||||
import { StationsRepository } from '../data/stations.repository';
|
||||
import { authMiddleware } from '../../../core/security/auth.middleware';
|
||||
import pool from '../../../core/config/database';
|
||||
|
||||
export function registerStationsRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
// Initialize layers
|
||||
const repository = new StationsRepository(pool);
|
||||
const service = new StationsService(repository);
|
||||
const controller = new StationsController(service);
|
||||
|
||||
// Define routes
|
||||
router.post('/api/stations/search', authMiddleware, controller.search);
|
||||
router.post('/api/stations/save', authMiddleware, controller.save);
|
||||
router.get('/api/stations/saved', authMiddleware, controller.getSaved);
|
||||
router.delete('/api/stations/saved/:placeId', authMiddleware, controller.removeSaved);
|
||||
|
||||
return router;
|
||||
export const stationsRoutes: FastifyPluginAsync = async (
|
||||
fastify: FastifyInstance,
|
||||
_opts: FastifyPluginOptions
|
||||
) => {
|
||||
const stationsController = new StationsController();
|
||||
|
||||
// POST /api/stations/search - Search nearby stations
|
||||
fastify.post<{ Body: StationSearchBody }>('/stations/search', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: stationsController.searchStations.bind(stationsController)
|
||||
});
|
||||
|
||||
// POST /api/stations/save - Save a station to user's favorites
|
||||
fastify.post<{ Body: SaveStationBody }>('/stations/save', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: stationsController.saveStation.bind(stationsController)
|
||||
});
|
||||
|
||||
// GET /api/stations/saved - Get user's saved stations
|
||||
fastify.get('/stations/saved', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: stationsController.getSavedStations.bind(stationsController)
|
||||
});
|
||||
|
||||
// DELETE /api/stations/saved/:placeId - Remove saved station
|
||||
fastify.delete<{ Params: StationParams }>('/stations/saved/:placeId', {
|
||||
preHandler: fastify.authenticate,
|
||||
handler: stationsController.removeSavedStation.bind(stationsController)
|
||||
});
|
||||
};
|
||||
|
||||
// For backward compatibility during migration
|
||||
export function registerStationsRoutes() {
|
||||
throw new Error('registerStationsRoutes is deprecated - use stationsRoutes Fastify plugin instead');
|
||||
}
|
||||
@@ -46,4 +46,23 @@ export interface SavedStation {
|
||||
isFavorite: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// Fastify-specific types for HTTP handling
|
||||
export interface StationSearchBody {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
radius?: number;
|
||||
fuelType?: 'regular' | 'premium' | 'diesel';
|
||||
}
|
||||
|
||||
export interface SaveStationBody {
|
||||
placeId: string;
|
||||
nickname?: string;
|
||||
notes?: string;
|
||||
isFavorite?: boolean;
|
||||
}
|
||||
|
||||
export interface StationParams {
|
||||
placeId: string;
|
||||
}
|
||||
@@ -13,5 +13,5 @@ export type {
|
||||
SavedStation
|
||||
} from './domain/stations.types';
|
||||
|
||||
// Internal: Register routes
|
||||
export { registerStationsRoutes } from './api/stations.routes';
|
||||
// Internal: Register routes with Fastify app
|
||||
export { stationsRoutes, registerStationsRoutes } from './api/stations.routes';
|
||||
|
||||
Reference in New Issue
Block a user