9e7ab0b16f
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.
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
import { json, type RequestHandler } from '@sveltejs/kit';
|
||
import type { BriefRecipeType } from '$types/types';
|
||
import { Recipe } from '$models/Recipe';
|
||
import { dbConnect } from '$utils/db';
|
||
|
||
export const GET: RequestHandler = async ({ url, locals }) => {
|
||
await dbConnect();
|
||
|
||
const query = url.searchParams.get('q')?.toLowerCase().trim() || '';
|
||
const category = url.searchParams.get('category');
|
||
|
||
// Support both single tag (backwards compat) and multiple tags
|
||
const singleTag = url.searchParams.get('tag');
|
||
const multipleTags = url.searchParams.get('tags');
|
||
const tags = multipleTags
|
||
? multipleTags.split(',').map(t => t.trim()).filter(Boolean)
|
||
: (singleTag ? [singleTag] : []);
|
||
|
||
const icon = url.searchParams.get('icon');
|
||
|
||
// Support both single season (backwards compat) and multiple seasons
|
||
const singleSeason = url.searchParams.get('season');
|
||
const multipleSeasons = url.searchParams.get('seasons');
|
||
const seasons = multipleSeasons
|
||
? multipleSeasons.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n))
|
||
: (singleSeason ? [parseInt(singleSeason)].filter(n => !isNaN(n)) : []);
|
||
|
||
const favoritesOnly = url.searchParams.get('favorites') === 'true';
|
||
|
||
try {
|
||
// Build base query - only recipes with approved English translations
|
||
let dbQuery: any = {
|
||
'translations.en.translationStatus': 'approved'
|
||
};
|
||
|
||
// Apply filters based on context
|
||
if (category) {
|
||
dbQuery['translations.en.category'] = category;
|
||
}
|
||
|
||
// Multi-tag AND logic: recipe must have ALL selected tags
|
||
if (tags.length > 0) {
|
||
dbQuery['translations.en.tags'] = { $all: tags };
|
||
}
|
||
|
||
if (icon) {
|
||
dbQuery.icon = icon; // Icon is the same for both languages
|
||
}
|
||
|
||
// Multi-season OR logic: recipe in any selected season
|
||
if (seasons.length > 0) {
|
||
dbQuery.season = { $in: seasons }; // Season is the same for both languages
|
||
}
|
||
|
||
// Get all recipes matching base filters
|
||
let recipes = await Recipe.find(dbQuery).lean();
|
||
|
||
// Handle favorites filter
|
||
if (favoritesOnly && locals.session?.user) {
|
||
const { UserFavorites } = await import('../../../../models/UserFavorites');
|
||
const userFavorites = await UserFavorites.findOne({ username: locals.session.user.username });
|
||
if (userFavorites && userFavorites.favorites) {
|
||
const favoriteIds = userFavorites.favorites;
|
||
recipes = recipes.filter(recipe => favoriteIds.some(id => id.toString() === recipe._id?.toString()));
|
||
} else {
|
||
recipes = [];
|
||
}
|
||
}
|
||
|
||
// Transform to English brief format
|
||
let briefRecipes: BriefRecipeType[] = recipes.map(recipe => ({
|
||
_id: recipe._id,
|
||
name: recipe.translations.en.name,
|
||
short_name: recipe.translations.en.short_name,
|
||
tags: recipe.translations.en.tags || [],
|
||
category: recipe.translations.en.category,
|
||
icon: recipe.icon,
|
||
description: recipe.translations.en.description,
|
||
season: recipe.season,
|
||
dateModified: recipe.dateModified,
|
||
germanShortName: recipe.short_name
|
||
}));
|
||
|
||
// Apply text search if query provided
|
||
if (query) {
|
||
const searchTerms = query.normalize('NFD').replace(/\p{Diacritic}/gu, "").split(" ");
|
||
|
||
briefRecipes = briefRecipes.filter(recipe => {
|
||
const searchString = `${recipe.name} ${recipe.description || ''} ${recipe.tags?.join(' ') || ''}`.toLowerCase()
|
||
.normalize('NFD').replace(/\p{Diacritic}/gu, "").replace(/­|/g, '');
|
||
|
||
return searchTerms.every(term => searchString.includes(term));
|
||
});
|
||
}
|
||
|
||
return json(JSON.parse(JSON.stringify(briefRecipes)));
|
||
|
||
} catch (error) {
|
||
return json({ error: 'Search failed' }, { status: 500 });
|
||
}
|
||
};
|