Files
homepage/src/routes/api/recipes/items/[name]/+server.ts
Alexander Bocken 8560077759 refactor: reorganize components into domain subfolders and replace relative imports
Move components from flat src/lib/components/ into recipes/, faith/, and
cospend/ subdirectories. Replace ~144 relative imports across API routes
and lib files with $models, $utils, $types, and $lib aliases. Add $types
alias to svelte.config.js. Remove unused EditRecipe.svelte.
2026-02-11 09:49:11 +01:00

150 lines
5.1 KiB
TypeScript

import type { RequestHandler } from '@sveltejs/kit';
import { Recipe } from '$models/Recipe';
import { dbConnect } from '$utils/db';
import { error } from '@sveltejs/kit';
/**
* GET /api/recipes/items/[name]
* Fetch an English recipe by its English short_name
*/
export const GET: RequestHandler = async ({ params }) => {
await dbConnect();
try {
// Find recipe by English short_name and populate base recipe references
const recipe = await Recipe.findOne({
"translations.en.short_name": params.name
})
.populate({
path: 'translations.en.ingredients.baseRecipeRef',
select: 'short_name name ingredients instructions translations',
populate: {
path: 'ingredients.baseRecipeRef',
select: 'short_name name ingredients instructions translations',
populate: {
path: 'ingredients.baseRecipeRef',
select: 'short_name name ingredients instructions translations'
}
}
})
.populate({
path: 'translations.en.instructions.baseRecipeRef',
select: 'short_name name ingredients instructions translations',
populate: {
path: 'instructions.baseRecipeRef',
select: 'short_name name ingredients instructions translations',
populate: {
path: 'instructions.baseRecipeRef',
select: 'short_name name ingredients instructions translations'
}
}
})
.lean();
if (!recipe) {
throw error(404, 'Recipe not found');
}
if (!recipe.translations?.en) {
throw error(404, 'English translation not available for this recipe');
}
// Return English translation with necessary metadata
let englishRecipe: any = {
_id: recipe._id,
short_name: recipe.translations.en.short_name,
name: recipe.translations.en.name,
description: recipe.translations.en.description,
preamble: recipe.translations.en.preamble || '',
addendum: recipe.translations.en.addendum || '',
note: recipe.translations.en.note || '',
category: recipe.translations.en.category,
tags: recipe.translations.en.tags || [],
ingredients: recipe.translations.en.ingredients || [],
instructions: recipe.translations.en.instructions || [],
images: recipe.images || [], // Use original images with full paths, but English alt/captions
// Use English translations for timing/metadata fields when available, fallback to German version
icon: recipe.icon || '',
dateCreated: recipe.dateCreated,
dateModified: recipe.dateModified,
season: recipe.season || [],
baking: recipe.translations.en.baking || recipe.baking || { temperature: '', length: '', mode: '' },
preparation: recipe.translations.en.preparation || recipe.preparation || '',
fermentation: recipe.translations.en.fermentation || recipe.fermentation || { bulk: '', final: '' },
portions: recipe.translations.en.portions || recipe.portions || '',
cooking: recipe.translations.en.cooking || recipe.cooking || '',
total_time: recipe.translations.en.total_time || recipe.total_time || '',
// Include translation status for display
translationStatus: recipe.translations.en.translationStatus,
// Include German short_name for language switcher
germanShortName: recipe.short_name,
};
// Recursively map populated base recipe refs to resolvedRecipe field
function mapBaseRecipeRefs(items: any[]): any[] {
return items.map((item: any) => {
if (item.type === 'reference' && item.baseRecipeRef) {
const resolvedRecipe = { ...item.baseRecipeRef };
// Recursively map nested baseRecipeRefs
if (resolvedRecipe.ingredients) {
resolvedRecipe.ingredients = mapBaseRecipeRefs(resolvedRecipe.ingredients);
}
if (resolvedRecipe.instructions) {
resolvedRecipe.instructions = mapBaseRecipeRefs(resolvedRecipe.instructions);
}
return { ...item, resolvedRecipe };
}
return item;
});
}
if (englishRecipe.ingredients) {
englishRecipe.ingredients = mapBaseRecipeRefs(englishRecipe.ingredients);
}
if (englishRecipe.instructions) {
englishRecipe.instructions = mapBaseRecipeRefs(englishRecipe.instructions);
}
// Merge English alt/caption with original image paths
// Handle both array and single object (there's a bug in add page that sometimes saves as object)
const imagesArray = Array.isArray(recipe.images) ? recipe.images : (recipe.images ? [recipe.images] : []);
if (imagesArray.length > 0) {
const translatedImages = recipe.translations.en.images || [];
if (translatedImages.length > 0) {
englishRecipe.images = imagesArray.map((img: any, index: number) => ({
mediapath: img.mediapath,
alt: translatedImages[index]?.alt || img.alt || '',
caption: translatedImages[index]?.caption || img.caption || '',
}));
} else {
// No translated image captions, use German ones
englishRecipe.images = imagesArray.map((img: any) => ({
mediapath: img.mediapath,
alt: img.alt || '',
caption: img.caption || '',
}));
}
}
return new Response(JSON.stringify(englishRecipe), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (err: any) {
console.error('Error fetching English recipe:', err);
if (err.status) {
throw err;
}
throw error(500, 'Failed to fetch recipe');
}
};