Files
homepage/src/routes/api/fitness/custom-meals/+server.ts
T
Alexander a1b80862f5 feat: add "round off this day" nutrition suggestions
Suggest optimal 1-3 food combinations to fill remaining macro budget using
weighted least-squares solver over a curated pantry (~55 foods) plus user
favorites/recents. Recipes scored individually (no combining). Features:

- Combinatorial solver (singles, pairs, triples) with macro-weighted scoring
- MealTypePicker component (extracted from quick-log, shared)
- Hero card with fit%, macro delta icons (Beef/Droplet/Wheat), ingredient
  cards, animated +/X toggle for logging
- Responsive layout: sidebar on mobile, center column on desktop
- MongoDB cache with ±5% tolerance, SSR on cache hit, TTL auto-expiry
- Cache invalidation on food-log/favorites/custom-meals CRUD
- Recipe per100g backfill admin endpoint
2026-04-09 20:47:32 +02:00

35 lines
1.1 KiB
TypeScript

import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { requireAuth } from '$lib/server/middleware/auth';
import { dbConnect } from '$utils/db';
import { CustomMeal } from '$models/CustomMeal';
import { RoundOffCache } from '$models/RoundOffCache';
export const GET: RequestHandler = async ({ locals }) => {
const user = await requireAuth(locals);
await dbConnect();
const meals = await CustomMeal.find({ createdBy: user.nickname }).sort({ updatedAt: -1 }).lean();
return json({ meals });
};
export const POST: RequestHandler = async ({ request, locals }) => {
const user = await requireAuth(locals);
await dbConnect();
const body = await request.json();
const { name, ingredients } = body;
if (!name?.trim()) throw error(400, 'name is required');
if (!Array.isArray(ingredients) || ingredients.length === 0) throw error(400, 'At least one ingredient is required');
const meal = await CustomMeal.create({
name: name.trim(),
ingredients,
createdBy: user.nickname,
});
RoundOffCache.deleteMany({ createdBy: user.nickname }).catch(() => {});
return json(meal.toObject(), { status: 201 });
};