/** * 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=` — 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. * * @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 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 { items: dumpItems, total: dumpTotal } = searchDumps( q, page, limit, requestingUserId, ); const users = searchUsersGlobal(q, 20); const playlists = searchPlaylists(q, 20, requestingUserId); const responseBody: APIResponse = { success: true, data: { dumps: { items: dumpItems, total: dumpTotal, hasMore: page * limit < dumpTotal, }, users, playlists, }, }; ctx.response.body = responseBody; }); export default router;