boredgame/src/routes/(app)/search/+page.server.ts

214 lines
5.8 KiB
TypeScript

import { error } from '@sveltejs/kit';
import { zod } from 'sveltekit-superforms/adapters';
import { superValidate } from 'sveltekit-superforms/server';
import kebabCase from 'just-kebab-case';
import type { GameType, SearchQuery } from '$lib/types';
import { mapAPIGameToBoredGame } from '$lib/utils/gameMapper.js';
import { search_schema } from '$lib/zodValidation';
import type { PageServerLoad } from '../$types.js';
import type { BggThingDto } from 'boardgamegeekclient/dist/esm/dto/index.js';
import { createOrUpdateGameMinimal } from '$lib/utils/db/gameUtils';
async function searchForGames(
locals: App.Locals,
eventFetch,
urlQueryParams: SearchQuery
) {
try {
console.log('urlQueryParams search games', urlQueryParams);
const headers = new Headers();
headers.set('Content-Type', 'application/json');
const requestInit: RequestInit = {
method: 'GET',
headers
};
const url = `/api/game/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
console.log('Calling internal api', url);
const response = await eventFetch(url, requestInit);
console.log('response from internal api', response);
if (response.status !== 404 && !response.ok) {
console.log('Status from internal api not 200', response.status);
error(response.status);
}
const games = await response.json();
console.log('games from DB', games);
const gameNameSearch = urlQueryParams.get('q');
let totalCount = games?.length || 0;
if (
totalCount === 0 ||
!games.find((game: GameType) => game.slug === kebabCase(gameNameSearch))
) {
console.log('No games found in DB for', gameNameSearch);
const externalResponse = await eventFetch(
`/api/external/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
requestInit
);
console.log('Back from external search', externalResponse);
if (!externalResponse.ok) {
console.log('Status not 200', externalResponse.status);
error(externalResponse.status);
}
if (externalResponse.ok) {
const gameResponse = await externalResponse.json();
console.log('response from external api', gameResponse);
const gameList: BggThingDto[] = gameResponse?.games;
totalCount = gameResponse?.totalCount;
console.log('totalCount', totalCount);
for (const game of gameList) {
console.log(
`Retrieving simplified external game details for id: ${game.id} with name ${game.name}`
);
const externalGameResponse = await eventFetch(
`/api/external/game/${game.id}?simplified=true`
);
if (externalGameResponse.ok) {
const externalGame = await externalGameResponse.json();
console.log('externalGame', externalGame);
const boredGame = mapAPIGameToBoredGame(externalGame);
games.push(createOrUpdateGameMinimal(locals, boredGame, externalGame.id));
}
}
}
}
return {
totalCount,
games
};
} catch (e) {
console.log(`Error searching board games ${e}`);
}
return {
totalCount: 0,
games: []
};
}
const defaults = {
limit: 10,
skip: 0,
order: 'name',
sort: 'asc',
q: '',
exact: false,
};
export const load: PageServerLoad = async ({ locals, fetch, url }) => {
const searchParams = Object.fromEntries(url?.searchParams);
console.log('searchParams', searchParams);
searchParams.order = searchParams.order || defaults.order;
searchParams.sort = searchParams.sort || defaults.sort;
searchParams.q = searchParams.q || defaults.q;
const form = await superValidate({
...searchParams,
skip: Number(searchParams.skip || defaults.skip),
limit: Number(searchParams.limit || defaults.limit),
exact: searchParams.exact ? searchParams.exact === 'true' : defaults.exact
}, zod(search_schema));
const queryParams: SearchQuery = {
limit: form.data?.limit,
skip: form.data?.skip,
q: form.data?.q,
exact: form.data?.exact
};
try {
if (form.data?.q === '') {
return {
form,
searchData: {
totalCount: 0,
games: [],
wishlists: []
}
};
}
if (form.data?.minAge) {
if (form.data?.exactMinAge) {
queryParams.min_age = form.data?.minAge;
} else {
queryParams.gt_min_age = form.data?.minAge === 1 ? 0 : form.data?.minAge - 1;
}
}
if (form.data?.minPlayers) {
if (form.data?.exactMinPlayers) {
queryParams.min_players = form.data?.minPlayers;
} else {
queryParams.gt_min_players = form.data?.minPlayers === 1 ? 0 : form.data?.minPlayers - 1;
}
}
if (form.data?.maxPlayers) {
if (form.data?.exactMaxPlayers) {
queryParams.max_players = form.data?.maxPlayers;
} else {
queryParams.lt_max_players = form.data?.maxPlayers + 1;
}
}
const newQueryParams: Record<string, string> = {};
for (const key in queryParams) {
newQueryParams[key] = `${queryParams[key as keyof SearchQuery]}`;
}
const urlQueryParams = new URLSearchParams(newQueryParams);
const searchData = await searchForGames(locals, fetch, urlQueryParams);
console.log('search data', JSON.stringify(searchData, null, 2));
return {
form,
// modifyListForm,
searchData,
wishlists: []
};
} catch (e) {
console.log(`Error searching board games ${e}`);
}
console.log('returning default no data')
return {
form,
searchData: {
totalCount: 0,
games: []
},
wishlists: []
};
};
export const actions = {
random: async ({ request, locals, fetch }): Promise<any> => {
const form = await superValidate(request, zod(search_schema));
const queryParams: SearchQuery = {
order_by: 'rank',
ascending: false,
random: true,
fields:
'id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description'
};
const newQueryParams: Record<string, string> = {};
for (const key in queryParams) {
newQueryParams[key] = `${queryParams[key as keyof SearchQuery]}`;
}
const urlQueryParams = new URLSearchParams(newQueryParams);
return {
form,
searchData: await searchForGames(locals, fetch, urlQueryParams)
};
}
};