Files
family_wishlist/apps/backend/src/modules/public/public.routes.ts

59 lines
2.2 KiB
TypeScript

import type { FastifyInstance } from 'fastify';
import { markSeenSchema } from '@family-wishlist/shared';
import { NotFoundError } from '../../utils/errors.js';
export default async function publicRoutes(app: FastifyInstance) {
app.get('/:slug', async (request) => {
const { slug } = request.params as { slug: string };
const user = await app.prisma.user.findUnique({
where: { slug },
select: { slug: true, displayName: true, bio: true, avatarUrl: true },
});
if (!user) throw new NotFoundError('Profile');
return user;
});
app.get('/:slug/wishes', async (request) => {
const { slug } = request.params as { slug: string };
const user = await app.prisma.user.findUnique({ where: { slug }, select: { id: true } });
if (!user) throw new NotFoundError('Profile');
const wishes = await app.prisma.wish.findMany({
where: { userId: user.id, status: { in: ['ACTIVE', 'COMPLETED'] } },
orderBy: [{ status: 'asc' }, { createdAt: 'desc' }],
});
const wishIds = wishes.map((w) => w.id);
const seen = wishIds.length
? await app.prisma.guestView.findMany({
where: { guestId: request.guestId, wishId: { in: wishIds } },
select: { wishId: true },
})
: [];
const seenSet = new Set(seen.map((s) => s.wishId));
return wishes.map((w) => ({
...w,
isNewForGuest: w.status === 'ACTIVE' && !seenSet.has(w.id),
}));
});
app.post('/:slug/views', async (request) => {
const { slug } = request.params as { slug: string };
const body = markSeenSchema.parse(request.body);
const user = await app.prisma.user.findUnique({ where: { slug }, select: { id: true } });
if (!user) throw new NotFoundError('Profile');
// Filter wishIds to those that actually belong to this user (avoid cross-user pollution).
const owned = await app.prisma.wish.findMany({
where: { userId: user.id, id: { in: body.wishIds } },
select: { id: true },
});
if (owned.length === 0) return { marked: 0 };
const data = owned.map((w) => ({ guestId: request.guestId, wishId: w.id }));
const res = await app.prisma.guestView.createMany({ data, skipDuplicates: true });
return { marked: res.count };
});
}