feat(api): wishlist endpoints (list, add, remove, like, unlike)
GET /api/wishlist?sort=popular|newest|oldest&profile_id=…
POST /api/wishlist { recipe_id, profile_id? }
DELETE /api/wishlist/[recipe_id]
PUT /api/wishlist/[recipe_id]/like { profile_id }
DELETE /api/wishlist/[recipe_id]/like { profile_id }
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
40
src/routes/api/wishlist/+server.ts
Normal file
40
src/routes/api/wishlist/+server.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { z } from 'zod';
|
||||
import { getDb } from '$lib/server/db';
|
||||
import {
|
||||
addToWishlist,
|
||||
listWishlist,
|
||||
type SortKey
|
||||
} from '$lib/server/wishlist/repository';
|
||||
|
||||
const AddSchema = z.object({
|
||||
recipe_id: z.number().int().positive(),
|
||||
profile_id: z.number().int().positive().nullable().optional()
|
||||
});
|
||||
|
||||
const VALID_SORTS: readonly SortKey[] = ['popular', 'newest', 'oldest'] as const;
|
||||
|
||||
function parseSort(raw: string | null): SortKey {
|
||||
return VALID_SORTS.includes(raw as SortKey) ? (raw as SortKey) : 'popular';
|
||||
}
|
||||
|
||||
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 sort = parseSort(url.searchParams.get('sort'));
|
||||
const profileId = parseProfileId(url.searchParams.get('profile_id'));
|
||||
return json({ sort, entries: listWishlist(getDb(), profileId, sort) });
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const body = await request.json().catch(() => null);
|
||||
const parsed = AddSchema.safeParse(body);
|
||||
if (!parsed.success) error(400, { message: 'Invalid body' });
|
||||
addToWishlist(getDb(), parsed.data.recipe_id, parsed.data.profile_id ?? null);
|
||||
return json({ ok: true }, { status: 201 });
|
||||
};
|
||||
16
src/routes/api/wishlist/[recipe_id]/+server.ts
Normal file
16
src/routes/api/wishlist/[recipe_id]/+server.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { getDb } from '$lib/server/db';
|
||||
import { removeFromWishlist } from '$lib/server/wishlist/repository';
|
||||
|
||||
function parseId(raw: string): number {
|
||||
const id = Number(raw);
|
||||
if (!Number.isInteger(id) || id <= 0) error(400, { message: 'Invalid recipe_id' });
|
||||
return id;
|
||||
}
|
||||
|
||||
export const DELETE: RequestHandler = async ({ params }) => {
|
||||
const id = parseId(params.recipe_id!);
|
||||
removeFromWishlist(getDb(), id);
|
||||
return json({ ok: true });
|
||||
};
|
||||
31
src/routes/api/wishlist/[recipe_id]/like/+server.ts
Normal file
31
src/routes/api/wishlist/[recipe_id]/like/+server.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { z } from 'zod';
|
||||
import { getDb } from '$lib/server/db';
|
||||
import { likeWish, unlikeWish } from '$lib/server/wishlist/repository';
|
||||
|
||||
const Schema = z.object({ profile_id: z.number().int().positive() });
|
||||
|
||||
function parseId(raw: string): number {
|
||||
const id = Number(raw);
|
||||
if (!Number.isInteger(id) || id <= 0) error(400, { message: 'Invalid recipe_id' });
|
||||
return id;
|
||||
}
|
||||
|
||||
export const PUT: RequestHandler = async ({ params, request }) => {
|
||||
const id = parseId(params.recipe_id!);
|
||||
const body = await request.json().catch(() => null);
|
||||
const parsed = Schema.safeParse(body);
|
||||
if (!parsed.success) error(400, { message: 'Invalid body' });
|
||||
likeWish(getDb(), id, parsed.data.profile_id);
|
||||
return json({ ok: true });
|
||||
};
|
||||
|
||||
export const DELETE: RequestHandler = async ({ params, request }) => {
|
||||
const id = parseId(params.recipe_id!);
|
||||
const body = await request.json().catch(() => null);
|
||||
const parsed = Schema.safeParse(body);
|
||||
if (!parsed.success) error(400, { message: 'Invalid body' });
|
||||
unlikeWish(getDb(), id, parsed.data.profile_id);
|
||||
return json({ ok: true });
|
||||
};
|
||||
Reference in New Issue
Block a user