tomodachi-share/src/app/api/search/route.ts
2025-11-16 21:27:24 +00:00

79 lines
2.5 KiB
TypeScript

import { NextRequest } from "next/server";
import crypto from "crypto";
import seedrandom from "seedrandom";
import { searchSchema } from "@/lib/schemas";
import { RateLimit } from "@/lib/rate-limit";
import { prisma } from "@/lib/prisma";
import { Prisma } from "@prisma/client";
export async function GET(request: NextRequest) {
const rateLimit = new RateLimit(request, 24, "/api/search");
const check = await rateLimit.handle();
if (check) return check;
const parsed = searchSchema.safeParse(Object.fromEntries(request.nextUrl.searchParams));
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const { q: query, sort, tags, gender, page = 1, limit = 24, seed } = parsed.data;
const where: Prisma.MiiWhereInput = {
// Searching
...(query && {
OR: [{ name: { contains: query, mode: "insensitive" } }, { tags: { has: query } }, { description: { contains: query, mode: "insensitive" } }],
}),
// Tag filtering
...(tags && tags.length > 0 && { tags: { hasEvery: tags } }),
// Gender
...(gender && { gender: { equals: gender } }),
};
const skip = (page - 1) * limit;
if (sort === "random") {
// Use seed for consistent random results
const randomSeed = seed || crypto.randomInt(0, 1_000_000_000);
// Get all IDs that match the where conditions
const matchingIds = await prisma.mii.findMany({
where,
select: { id: true },
});
if (matchingIds.length === 0) return rateLimit.sendResponse({ error: "No Miis found!" }, 404);
const rng = seedrandom(randomSeed.toString());
// Randomize all IDs using the Durstenfeld algorithm
for (let i = matchingIds.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i + 1));
[matchingIds[i], matchingIds[j]] = [matchingIds[j], matchingIds[i]];
}
// Convert to number[] array and return paginated results
return rateLimit.sendResponse(matchingIds.slice(skip, skip + limit).map((i) => i.id));
} else {
// Sorting by likes, newest, or oldest
let orderBy: Prisma.MiiOrderByWithRelationInput[];
if (sort === "likes") {
orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }];
} else if (sort === "oldest") {
orderBy = [{ createdAt: "asc" }, { name: "asc" }];
} else {
// default to newest
orderBy = [{ createdAt: "desc" }, { name: "asc" }];
}
const list = await prisma.mii.findMany({
where,
orderBy,
select: { id: true },
skip,
take: limit,
});
return rateLimit.sendResponse(list.map((mii) => mii.id));
}
}