diff --git a/src/routes/api/recipes/all/+server.ts b/src/routes/api/recipes/all/+server.ts index f6f9a30..fc77a5c 100644 --- a/src/routes/api/recipes/all/+server.ts +++ b/src/routes/api/recipes/all/+server.ts @@ -6,7 +6,19 @@ import { type AllRecipesSort } from '$lib/server/recipes/search-local'; -const VALID_SORTS = new Set(['name', 'rating', 'cooked', 'created']); +const VALID_SORTS = new Set([ + 'name', + 'rating', + 'cooked', + 'created', + 'viewed' +]); + +function parseProfileId(raw: string | null): number | null { + if (!raw) return null; + const n = Number(raw); + return Number.isInteger(n) && n > 0 ? n : null; +} export const GET: RequestHandler = async ({ url }) => { const sortRaw = (url.searchParams.get('sort') ?? 'name') as AllRecipesSort; @@ -17,6 +29,7 @@ export const GET: RequestHandler = async ({ url }) => { // one round-trip so document height matches and scroll-restore lands. const limit = Math.min(200, Math.max(1, Number(url.searchParams.get('limit') ?? 10))); const offset = Math.max(0, Number(url.searchParams.get('offset') ?? 0)); - const hits = listAllRecipesPaginated(getDb(), sortRaw, limit, offset); + const profileId = parseProfileId(url.searchParams.get('profile_id')); + const hits = listAllRecipesPaginated(getDb(), sortRaw, limit, offset, profileId); return json({ sort: sortRaw, limit, offset, hits }); }; diff --git a/tests/integration/recipe-views.test.ts b/tests/integration/recipe-views.test.ts index 4c15f59..fc1e150 100644 --- a/tests/integration/recipe-views.test.ts +++ b/tests/integration/recipe-views.test.ts @@ -237,3 +237,34 @@ describe('POST /api/recipes/[id]/view', () => { ).rejects.toMatchObject({ status: 404 }); }); }); + +// --------------------------------------------------------------------------- +// GET /api/recipes/all — sort=viewed + profile_id +// --------------------------------------------------------------------------- + +import { GET as allGet } from '../../src/routes/api/recipes/all/+server'; + +describe('GET /api/recipes/all sort=viewed', () => { + it('passes profile_id through and returns viewed-order hits', async () => { + const db = openInMemoryForTest(); + testDb.current = db; + const profile = createProfile(db, 'Test'); + const a = seedRecipe(db, 'Apfel'); + const b = seedRecipe(db, 'Birne'); + recordView(db, profile.id, b); + await new Promise((r) => setTimeout(r, 1100)); + recordView(db, profile.id, a); + + const url = new URL(`http://localhost/api/recipes/all?sort=viewed&profile_id=${profile.id}&limit=10`); + const res = await allGet({ url } as never); + expect(res.status).toBe(200); + const body = await res.json(); + expect(body.sort).toBe('viewed'); + expect(body.hits.map((h: { id: number }) => h.id)).toEqual([a, b]); + }); + + it('400 on invalid sort', async () => { + const url = new URL('http://localhost/api/recipes/all?sort=invalid'); + await expect(allGet({ url } as never)).rejects.toMatchObject({ status: 400 }); + }); +});