/** * Searches dumps, users and playlists at once, for the client's `Search` page. * Only the dumps are paged. Private dumps and playlists are found only by * their owner. * * - `GET /api/search?q=&page=&limit=&category=&playlistCategory=` — optional * sign-in. Answers a page of the matching dumps with their total, and up to * 20 matching users and playlists. A blank `q` finds nothing. `category` * keeps the dumps in that dump category, `playlistCategory` the playlists in * that playlist category; a slug that names none finds nothing. * * @module */ import { Router } from "@oak/oak"; import { parseOptionalAuth } from "../lib/auth.ts"; import { parsePagination } from "../lib/pagination.ts"; import { searchDumps } from "../services/dump-service.ts"; import { searchUsersGlobal } from "../services/user-service.ts"; import { searchPlaylists } from "../services/playlist-service.ts"; import { resolveCategoryFilter } from "../services/category-service.ts"; import type { APIResponse } from "../../shared/api.ts"; import type { SearchResults } from "../../shared/search.ts"; /** * Mounted by `api/main.ts` behind the error, Open Graph and CORS middleware. * An `APIException` thrown by a handler becomes the JSON error response there. */ const router: Router = new Router({ prefix: "/api/search" }); router.get("/", async (ctx) => { const params = ctx.request.url.searchParams; const q = (params.get("q") ?? "").trim(); const { page, limit } = parsePagination(params); const requestingUserId = (await parseOptionalAuth(ctx)) ?? undefined; if (!q) { const responseBody: APIResponse = { success: true, data: { dumps: { items: [], total: 0, hasMore: false }, users: [], playlists: [], }, }; ctx.response.body = responseBody; return; } const dumpCategoryId = resolveCategoryFilter("dump", params); const playlistCategoryId = resolveCategoryFilter( "playlist", params, "playlistCategory", ); const { items: dumpItems, total: dumpTotal } = dumpCategoryId === "missing" ? { items: [], total: 0 } : searchDumps(q, page, limit, requestingUserId, dumpCategoryId); const users = searchUsersGlobal(q, 20); const playlists = playlistCategoryId === "missing" ? [] : searchPlaylists(q, 20, requestingUserId, playlistCategoryId); const responseBody: APIResponse = { success: true, data: { dumps: { items: dumpItems, total: dumpTotal, hasMore: page * limit < dumpTotal, }, users, playlists, }, }; ctx.response.body = responseBody; }); export default router;