Files
flyer-crawler.projectium.com/src/routes/recipe.routes.ts
Torben Sorensen b929925a6e
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 7m32s
lots more tests !
2025-12-10 21:02:01 -08:00

105 lines
4.2 KiB
TypeScript

// src/routes/recipe.routes.ts
import { Router, type Request, type Response, type NextFunction } from 'express';
import crypto from 'crypto';
import * as db from '../services/db/index.db';
import { logger } from '../services/logger.server';
const router = Router();
/**
* GET /api/recipes/by-sale-percentage - Get recipes based on the percentage of their ingredients on sale.
*/
router.get('/by-sale-percentage', async (req: Request, res: Response, next: NextFunction) => {
const minPercentageStr = req.query.minPercentage as string || '50.0';
const minPercentage = parseFloat(minPercentageStr);
if (isNaN(minPercentage) || minPercentage < 0 || minPercentage > 100) {
return res.status(400).json({ message: 'Query parameter "minPercentage" must be a number between 0 and 100.' });
}
try {
const recipes = await db.recipeRepo.getRecipesBySalePercentage(minPercentage);
res.json(recipes);
} catch (error) {
logger.error('Error fetching recipes in /api/recipes/by-sale-percentage:', { error });
next(error);
}
});
/**
* GET /api/recipes/by-sale-ingredients - Get recipes by the minimum number of sale ingredients.
*/
router.get('/by-sale-ingredients', async (req: Request, res: Response, next: NextFunction) => {
try {
const minIngredientsStr = req.query.minIngredients as string || '3';
const minIngredients = parseInt(minIngredientsStr, 10);
if (isNaN(minIngredients) || minIngredients < 1) {
return res.status(400).json({ message: 'Query parameter "minIngredients" must be a positive integer.' });
}
const recipes = await db.recipeRepo.getRecipesByMinSaleIngredients(minIngredients);
res.json(recipes);
} catch (error) {
logger.error('Error fetching recipes in /api/recipes/by-sale-ingredients:', { error });
next(error);
}
});
/**
* GET /api/recipes/by-ingredient-and-tag - Find recipes by a specific ingredient and tag.
*/
router.get('/by-ingredient-and-tag', async (req: Request, res: Response, next: NextFunction) => {
try {
const { ingredient, tag } = req.query;
if (!ingredient || !tag) {
return res.status(400).json({ message: 'Both "ingredient" and "tag" query parameters are required.' });
}
const recipes = await db.recipeRepo.findRecipesByIngredientAndTag(ingredient as string, tag as string);
res.json(recipes);
} catch (error) {
logger.error('Error fetching recipes in /api/recipes/by-ingredient-and-tag:', { error });
next(error);
}
});
/**
* GET /api/recipes/:recipeId - Get a single recipe by its ID, including ingredients and tags.
*/
router.get('/:recipeId', async (req: Request, res: Response, next: NextFunction) => {
try {
const recipeId = parseInt(req.params.recipeId, 10);
if (isNaN(recipeId)) {
return res.status(400).json({ message: 'Invalid recipe ID provided.' });
}
const recipe = await db.recipeRepo.getRecipeById(recipeId);
if (!recipe) {
return res.status(404).json({ message: `Recipe with ID ${recipeId} not found.` });
}
res.json(recipe);
} catch (error) {
logger.error(`Error fetching recipe ID ${req.params.recipeId}:`, { error });
next(error);
}
});
/**
* Recipe-specific error handling middleware.
* This should be the last middleware added to this router.
* It catches any errors passed by `next(error)` in the routes above.
*/
router.use((error: Error, req: Request, res: Response, next: NextFunction) => {
// Generate a unique ID for this specific error occurrence.
const errorId = crypto.randomBytes(4).toString('hex');
// Log the full error details on the server for debugging.
logger.error(`[API /recipes] Error ID: ${errorId} - An unhandled error occurred in the recipe router.`, {
error: error.message,
stack: error.stack,
url: req.originalUrl,
method: req.method,
});
// Send a generic, safe response to the client.
res.status(500).json({ message: `An internal server error occurred. Please try again later. If the problem persists, please reference error ID: ${errorId}` });
});
export default router;