mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Update external API, add HTML entity package to convert from external API, and update schema.
This commit is contained in:
parent
649c2dc74f
commit
2a640ffd90
8 changed files with 81 additions and 103 deletions
10
package.json
10
package.json
|
|
@ -1,12 +1,14 @@
|
|||
{
|
||||
"name": "boredgame",
|
||||
"version": "0.0.2",
|
||||
"private": "true",
|
||||
"scripts": {
|
||||
"dev": "NODE_OPTIONS=\"--inspect\" vite dev --host",
|
||||
"build": "prisma generate && vite build",
|
||||
"package": "svelte-kit package",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"test:ui": "svelte-kit sync && playwright test --ui",
|
||||
"postinstall": "prisma generate",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
|
|
@ -16,7 +18,9 @@
|
|||
"site:update": "pnpm update -i -L",
|
||||
"db:studio": "prisma studio",
|
||||
"db:push": "prisma db push",
|
||||
"db:seed": "prisma db seed"
|
||||
"db:generate": "prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"i-changed-the-schema": "pnpm run db:push && pnpm run db:generate"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "ts-node --esm prisma/seed.ts"
|
||||
|
|
@ -65,7 +69,7 @@
|
|||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.1",
|
||||
"node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0",
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -77,6 +81,7 @@
|
|||
"@lucia-auth/adapter-prisma": "^3.0.1",
|
||||
"@lukeed/uuid": "^2.0.1",
|
||||
"@melt-ui/svelte": "^0.50.0",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@prisma/client": "5.3.1",
|
||||
"@types/feather-icons": "^4.29.1",
|
||||
"@vercel/og": "^0.5.13",
|
||||
|
|
@ -87,6 +92,7 @@
|
|||
"cookie": "^0.5.0",
|
||||
"feather-icons": "^4.29.1",
|
||||
"formsnap": "^0.0.9",
|
||||
"html-entities": "^2.4.0",
|
||||
"iconify-icon": "^1.0.8",
|
||||
"just-kebab-case": "^4.2.0",
|
||||
"loader": "^2.1.1",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ dependencies:
|
|||
'@melt-ui/svelte':
|
||||
specifier: ^0.50.0
|
||||
version: 0.50.0(svelte@4.2.1)
|
||||
'@paralleldrive/cuid2':
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2
|
||||
'@prisma/client':
|
||||
specifier: 5.3.1
|
||||
version: 5.3.1(prisma@5.3.1)
|
||||
|
|
@ -59,6 +62,9 @@ dependencies:
|
|||
formsnap:
|
||||
specifier: ^0.0.9
|
||||
version: 0.0.9(svelte@4.2.1)(sveltekit-superforms@1.7.2)(zod@3.22.2)
|
||||
html-entities:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
iconify-icon:
|
||||
specifier: ^1.0.8
|
||||
version: 1.0.8
|
||||
|
|
@ -1263,6 +1269,11 @@ packages:
|
|||
nanoid: 4.0.2
|
||||
svelte: 4.2.1
|
||||
|
||||
/@noble/hashes@1.3.2:
|
||||
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
|
||||
engines: {node: '>= 16'}
|
||||
dev: false
|
||||
|
||||
/@nodelib/fs.scandir@2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
|
|
@ -1281,6 +1292,12 @@ packages:
|
|||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.15.0
|
||||
|
||||
/@paralleldrive/cuid2@2.2.2:
|
||||
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.2
|
||||
dev: false
|
||||
|
||||
/@playwright/test@1.37.0:
|
||||
resolution: {integrity: sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
|
@ -2825,6 +2842,10 @@ packages:
|
|||
resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
/html-entities@2.4.0:
|
||||
resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==}
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
|
|
|||
|
|
@ -72,16 +72,16 @@ model Key {
|
|||
id String @id @unique
|
||||
hashed_password String?
|
||||
user_id String
|
||||
user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
|
||||
user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
|
||||
|
||||
@@index([user_id])
|
||||
@@map("keys")
|
||||
}
|
||||
|
||||
model Collection {
|
||||
id String @id @default(cuid())
|
||||
user_id String @unique
|
||||
user User @relation(references: [id], fields: [user_id])
|
||||
id String @id @default(cuid())
|
||||
user_id String @unique
|
||||
user User @relation(references: [id], fields: [user_id])
|
||||
items CollectionItem[]
|
||||
|
||||
@@index([user_id])
|
||||
|
|
@ -162,6 +162,7 @@ model Game {
|
|||
year_published Int? @db.Year
|
||||
min_players Int?
|
||||
max_players Int?
|
||||
playtime Int?
|
||||
min_playtime Int?
|
||||
max_playtime Int?
|
||||
min_age Int?
|
||||
|
|
@ -169,6 +170,7 @@ model Game {
|
|||
thumb_url String?
|
||||
url String?
|
||||
rules_url String?
|
||||
is_expansion Boolean @default(false)
|
||||
primary_publisher_id String?
|
||||
primary_publisher Publisher? @relation("PrimaryPublishers", references: [id], fields: [primary_publisher_id])
|
||||
primary_designer_id String?
|
||||
|
|
@ -206,14 +208,14 @@ model GameName {
|
|||
}
|
||||
|
||||
model Publisher {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
slug String
|
||||
external_id String @unique
|
||||
games Game[]
|
||||
primary_publisher Game[] @relation("PrimaryPublishers")
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
slug String
|
||||
external_id String @unique
|
||||
games Game[]
|
||||
primary_publisher Game[] @relation("PrimaryPublishers")
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
|
||||
@@fulltext([name])
|
||||
@@map("publishers")
|
||||
|
|
@ -233,13 +235,13 @@ model Category {
|
|||
}
|
||||
|
||||
model Mechanic {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
slug String
|
||||
games Game[]
|
||||
external_id String @unique
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
slug String
|
||||
games Game[]
|
||||
external_id String @unique
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
|
||||
@@fulltext([name])
|
||||
@@map("mechanics")
|
||||
|
|
@ -275,7 +277,7 @@ model Expansion {
|
|||
id String @id @default(cuid())
|
||||
name String
|
||||
year_published Int?
|
||||
baseGame Game? @relation(fields: [base_game_id], references: [id])
|
||||
base_game Game? @relation(fields: [base_game_id], references: [id])
|
||||
base_game_id String?
|
||||
external_id String @unique
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ async function main() {
|
|||
await prisma.mechanic.create({
|
||||
data: {
|
||||
name: mechanic.name,
|
||||
external_id: mechanic.id,
|
||||
external_id: createId(),
|
||||
slug: kebabCase(mechanic.name)
|
||||
}
|
||||
});
|
||||
|
|
@ -42,7 +42,7 @@ async function main() {
|
|||
await prisma.category.create({
|
||||
data: {
|
||||
name: category.name,
|
||||
external_id: category.id,
|
||||
external_id: createId(),
|
||||
slug: kebabCase(category.name)
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -135,16 +135,13 @@ export type GameType = {
|
|||
};
|
||||
|
||||
export type SearchQuery = {
|
||||
client_id: string;
|
||||
limit?: number;
|
||||
skip?: number;
|
||||
ids?: string[];
|
||||
list_id?: string;
|
||||
kickstarter?: boolean;
|
||||
random?: boolean;
|
||||
name?: string;
|
||||
exact?: boolean;
|
||||
fuzzy_match?: boolean;
|
||||
designer?: string;
|
||||
publisher?: string;
|
||||
artist?: string;
|
||||
|
|
@ -164,23 +161,11 @@ export type SearchQuery = {
|
|||
gt_max_playtime?: number;
|
||||
gt_min_age?: number;
|
||||
gt_year_published?: number;
|
||||
gt_price?: bigint;
|
||||
gt_msrp?: bigint;
|
||||
gt_discount?: bigint;
|
||||
gt_reddit_count?: number;
|
||||
gt_reddit_week_count?: number;
|
||||
gt_reddit_day_count?: number;
|
||||
lt_min_players?: number;
|
||||
lt_max_players?: number;
|
||||
lt_min_playtime?: number;
|
||||
lt_max_playtime?: number;
|
||||
lt_min_age?: number;
|
||||
lt_year_published?: number;
|
||||
lt_price?: bigint;
|
||||
lt_msrp?: bigint;
|
||||
lt_discount?: bigint;
|
||||
lt_reddit_count?: number;
|
||||
lt_reddit_week_count?: number;
|
||||
lt_reddit_day_count?: number;
|
||||
fields?: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ 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 { BggClient } from 'boardgamegeekclient';
|
||||
import type { BggThingDto } from 'boardgamegeekclient/dist/esm/dto/index.js';
|
||||
// import { listGameSchema } from '$lib/config/zod-schemas.js';
|
||||
|
||||
/**
|
||||
|
|
@ -78,29 +80,28 @@ async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
|||
console.log('games from DB', games);
|
||||
let totalCount = games?.length || 0;
|
||||
|
||||
if (!games || games.length === 0) {
|
||||
const url = new URL(
|
||||
`https://api.boardgameatlas.com/api/search${urlQueryParams ? `?${urlQueryParams}` : ''}`
|
||||
if (totalCount === 0) {
|
||||
console.log('No games found in DB for', urlQueryParams.get('name'));
|
||||
|
||||
const externalResponse = await eventFetch(
|
||||
`/api/external/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
|
||||
requestInit
|
||||
);
|
||||
const headers: HeadersInit = new Headers();
|
||||
headers.set('Content-Type', 'application/json');
|
||||
const requestInit: RequestInit = {
|
||||
method: 'GET',
|
||||
headers
|
||||
};
|
||||
const response = await fetch(url, requestInit);
|
||||
|
||||
console.log('Back from external search', externalResponse);
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('Status not 200', response.status);
|
||||
throw error(response.status);
|
||||
}
|
||||
|
||||
// const games: GameType[] = [];
|
||||
// let totalCount = 0;
|
||||
if (response.ok) {
|
||||
const gameResponse = await response.json();
|
||||
const gameList: GameType[] = gameResponse?.games;
|
||||
totalCount = gameResponse?.count;
|
||||
// // const games: GameType[] = [];
|
||||
// // let totalCount = 0;
|
||||
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);
|
||||
gameList.forEach((game) => {
|
||||
if (game?.min_players && game?.max_players) {
|
||||
|
|
@ -229,8 +230,8 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
console.log('searchParams', searchParams);
|
||||
searchParams.limit = searchParams.limit || `${defaults.limit}`;
|
||||
searchParams.skip = searchParams.skip || `${defaults.skip}`;
|
||||
searchParams.order = searchParams.order || 'asc';
|
||||
searchParams.sort = searchParams.sort || 'name';
|
||||
searchParams.order = searchParams.order || defaults.order;
|
||||
searchParams.sort = searchParams.sort || defaults.sort;
|
||||
const form = await superValidate(searchParams, search_schema);
|
||||
// const modifyListForm = await superValidate(listGameSchema);
|
||||
|
||||
|
|
@ -239,8 +240,6 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
ascending: false,
|
||||
limit: form.data?.limit,
|
||||
skip: form.data?.skip,
|
||||
client_id: BOARD_GAME_ATLAS_CLIENT_ID,
|
||||
fuzzy_match: true,
|
||||
name: form.data?.q
|
||||
};
|
||||
|
||||
|
|
|
|||
13
src/routes/api/external/search/+server.ts
vendored
13
src/routes/api/external/search/+server.ts
vendored
|
|
@ -6,7 +6,8 @@ import { search_schema } from '$lib/zodValidation.js';
|
|||
|
||||
export async function GET({ url, locals, params }) {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
const q = searchParams?.q || '';
|
||||
console.log('searchParams external', searchParams);
|
||||
const name = searchParams?.name || '';
|
||||
const exact = parseInt(searchParams.exact) || 0;
|
||||
const limit = parseInt(searchParams?.limit) || 10;
|
||||
const skip = parseInt(searchParams?.skip) || 0;
|
||||
|
|
@ -14,7 +15,7 @@ export async function GET({ url, locals, params }) {
|
|||
// TODO: Debounce and throttle
|
||||
try {
|
||||
search_schema.parse({
|
||||
q,
|
||||
q: name,
|
||||
limit,
|
||||
skip
|
||||
});
|
||||
|
|
@ -29,7 +30,7 @@ export async function GET({ url, locals, params }) {
|
|||
|
||||
const client = BggClient.Create();
|
||||
const request: ISearchRequest = {
|
||||
query: q,
|
||||
query: name,
|
||||
exact,
|
||||
type: ['boardgame', 'boardgameaccessory', 'boardgameexpansion']
|
||||
};
|
||||
|
|
@ -46,7 +47,11 @@ export async function GET({ url, locals, params }) {
|
|||
if (end > result.total) {
|
||||
end = result.total;
|
||||
}
|
||||
const apiResponse = result.items.slice(start, end);
|
||||
const games = result.items.slice(start, end);
|
||||
const apiResponse = {
|
||||
totalCount: response[0].total,
|
||||
games
|
||||
};
|
||||
|
||||
console.log('Response from BGG', JSON.stringify(result, null, 2));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +1,16 @@
|
|||
import { error, json } from '@sveltejs/kit';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import z from 'zod';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { search_schema } from '$lib/zodValidation.js';
|
||||
|
||||
// Search a user's collection
|
||||
export const GET = async ({ url, locals, params, request }) => {
|
||||
// try {
|
||||
// z.parse;
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// return error(500, { message: 'Something went wrong' });
|
||||
// }
|
||||
|
||||
// let games = await prisma.game.findMany({
|
||||
// where: {
|
||||
// name: {
|
||||
// search: urlQueryParams?.name
|
||||
// },
|
||||
// min_players: {
|
||||
// gte: urlQueryParams?.min_players || 0
|
||||
// },
|
||||
// max_players: {
|
||||
// lte: urlQueryParams?.max_players || 100
|
||||
// },
|
||||
// min_playtime: {
|
||||
// gte: urlQueryParams?.min_playtime || 0
|
||||
// },
|
||||
// max_playtime: {
|
||||
// lte: urlQueryParams?.max_playtime || 5000
|
||||
// },
|
||||
// min_age: {
|
||||
// gte: urlQueryParams?.min_age || 0
|
||||
// }
|
||||
// },
|
||||
// skip: urlQueryParams?.skip,
|
||||
// take: urlQueryParams?.limit,
|
||||
// orderBy: {
|
||||
// name: 'asc'
|
||||
// }
|
||||
// });
|
||||
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
const q = searchParams?.q || '';
|
||||
const limit = parseInt(searchParams?.limit) || 10;
|
||||
const skip = parseInt(searchParams?.skip) || 0;
|
||||
const order: Prisma.SortOrder = <Prisma.SortOrder>searchParams?.order || 'asc';
|
||||
const sort = searchParams?.sort || 'name';
|
||||
// const session = await locals.auth.validate();
|
||||
console.log('url', url);
|
||||
// console.log('username', locals?.user?.id);
|
||||
|
||||
try {
|
||||
const orderBy = { [sort]: order };
|
||||
|
|
|
|||
Loading…
Reference in a new issue