Fixing all the root imports, installing and adding logging based on auth example.

This commit is contained in:
Bradley Shellnut 2023-05-28 23:34:39 -07:00
parent f35bddc27b
commit 16aad6696d
33 changed files with 707 additions and 353 deletions

View file

@ -17,14 +17,14 @@
"seed": "ts-node --esm prisma/seed.ts" "seed": "ts-node --esm prisma/seed.ts"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.34.1", "@playwright/test": "^1.34.3",
"@rgossiaux/svelte-headlessui": "1.0.2", "@rgossiaux/svelte-headlessui": "1.0.2",
"@rgossiaux/svelte-heroicons": "^0.1.2", "@rgossiaux/svelte-heroicons": "^0.1.2",
"@sveltejs/adapter-auto": "^1.0.3", "@sveltejs/adapter-auto": "^1.0.3",
"@sveltejs/adapter-vercel": "^1.0.6", "@sveltejs/adapter-vercel": "^1.0.6",
"@sveltejs/kit": "^1.18.0", "@sveltejs/kit": "^1.19.0",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/node": "^18.16.14", "@types/node": "^18.16.16",
"@typescript-eslint/eslint-plugin": "^5.59.7", "@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7", "@typescript-eslint/parser": "^5.59.7",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
@ -52,12 +52,13 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tslib": "^2.5.2", "tslib": "^2.5.2",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"vite": "^4.3.8", "vite": "^4.3.9",
"vitest": "^0.25.3", "vitest": "^0.25.3",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@axiomhq/axiom-node": "^0.12.0",
"@fontsource/fira-mono": "^4.5.10", "@fontsource/fira-mono": "^4.5.10",
"@iconify-icons/line-md": "^1.2.22", "@iconify-icons/line-md": "^1.2.22",
"@iconify-icons/mdi": "^1.2.46", "@iconify-icons/mdi": "^1.2.46",
@ -76,6 +77,7 @@
"open-props": "^1.5.8", "open-props": "^1.5.8",
"svelte-lazy": "^1.2.1", "svelte-lazy": "^1.2.1",
"svelte-lazy-loader": "^1.0.0", "svelte-lazy-loader": "^1.0.0",
"sveltekit-flash-message": "^0.11.3",
"zod-to-json-schema": "^3.21.1" "zod-to-json-schema": "^3.21.1"
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -55,4 +55,99 @@ model AuthKey {
enum Role { enum Role {
USER USER
ADMIN ADMIN
} }
model Game {
id String @id
name String
description String?
yearPublished Int?
minPlayers Int?
maxPlayers Int?
minPlaytime Int?
maxPlaytime Int?
minAge Int?
imageUrl String?
thumbUrl String?
url String?
rulesUrl String?
weightAmount Float?
weightUnits String?
bggId String?
bggUrl String?
primaryPublisher Publisher?
categories Category[]
mechanics Mechanic[]
designers Designer[]
publishers Publisher[]
artists Artist[]
names String[] @db.Array
expansions Expansion[]
@@index([game_id])
@@map("games")
}
model Publisher {
id String @id
name String
games Game[] @relation(references: [id], fields: [game_id])
primaryPublisher Game[] @relation("PrimaryPublisher")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("publishers")
}
model Category {
id String @id
name String
slug String
games Game[] @relation("GameCategories")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("categories")
}
model Mechanic {
id String @id
name String
games Game[] @relation("GameMechanics")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("mechanics")
}
model Designer {
id String @id
name String
games Game[] @relation("GameDesigners")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("designers")
}
model Artist {
id String @id
name String
games Game[] @relation("GameArtists")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("artists")
}
model Expansion {
id String @id
name String
yearPublished Int?
baseGame Game? @relation("Expansions", fields: [baseGameId], references: [id])
baseGameId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("expansions")
}

13
src/app.d.ts vendored
View file

@ -4,9 +4,22 @@
// src/app.d.ts // src/app.d.ts
declare global { declare global {
namespace App { namespace App {
interface PageData {
flash?: { type: 'success' | 'error'; message: string };
}
interface Locals { interface Locals {
auth: import('lucia-auth').AuthRequest; auth: import('lucia-auth').AuthRequest;
user: Lucia.UserAttributes; user: Lucia.UserAttributes;
startTimer: number;
error: string;
errorId: string;
errorStackTrace: string;
message: unknown;
track: unknown;
}
interface Error {
code?: string;
errorId?: string;
} }
} }
} }

View file

@ -1,7 +1,33 @@
import { redirect, type Handle } from '@sveltejs/kit'; import { redirect, type Handle } from '@sveltejs/kit';
import type { HandleServerError } from '@sveltejs/kit';
import { auth } from '$lib/server/lucia'; import { auth } from '$lib/server/lucia';
import log from '$lib/server/log';
import { dev } from '$app/environment';
export const handleError: HandleServerError = async ({ error, event }) => {
const errorId = crypto.randomUUID();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
event.locals.error = error?.toString() || undefined;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
event.locals.errorStackTrace = error?.stack || undefined;
event.locals.errorId = errorId;
if (!dev) {
log(500, event);
}
return {
message: 'An unexpected error occurred.',
errorId
};
};
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const startTimer = Date.now();
event.locals.startTimer = startTimer;
event.locals.auth = auth.handleRequest(event); event.locals.auth = auth.handleRequest(event);
console.log(JSON.stringify(event)); console.log(JSON.stringify(event));
if (event.locals?.auth) { if (event.locals?.auth) {
@ -13,5 +39,9 @@ export const handle: Handle = async ({ event, resolve }) => {
} }
} }
return await resolve(event); const response = await resolve(event);
if (!dev) {
log(response.status, event);
}
return response;
}; };

View file

@ -6,8 +6,8 @@
DialogOverlay, DialogOverlay,
DialogTitle DialogTitle
} from '@rgossiaux/svelte-headlessui'; } from '@rgossiaux/svelte-headlessui';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { collectionStore } from '$root/lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
function clearCollection() { function clearCollection() {

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { wishlistStore } from '$root/lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import DefaultDialog from './DefaultDialog.svelte'; import DefaultDialog from './DefaultDialog.svelte';
function clearWishlist() { function clearWishlist() {

View file

@ -7,7 +7,7 @@
DialogOverlay, DialogOverlay,
DialogTitle DialogTitle
} from '@rgossiaux/svelte-headlessui'; } from '@rgossiaux/svelte-headlessui';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
export let title: string; export let title: string;
export let description: string; export let description: string;

View file

@ -6,9 +6,9 @@
DialogOverlay, DialogOverlay,
DialogTitle DialogTitle
} from '@rgossiaux/svelte-headlessui'; } from '@rgossiaux/svelte-headlessui';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { collectionStore } from '$root/lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { removeFromCollection } from '$root/lib/util/manipulateCollection'; import { removeFromCollection } from '$lib/util/manipulateCollection';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
function removeGame() { function removeGame() {

View file

@ -6,9 +6,9 @@
DialogOverlay, DialogOverlay,
DialogTitle DialogTitle
} from '@rgossiaux/svelte-headlessui'; } from '@rgossiaux/svelte-headlessui';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { wishlistStore } from '$root/lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import { removeFromWishlist } from '$root/lib/util/manipulateWishlist'; import { removeFromWishlist } from '$lib/util/manipulateWishlist';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
function removeGame() { function removeGame() {

View file

@ -7,12 +7,12 @@
import Button from '$lib/components/button/index.svelte'; import Button from '$lib/components/button/index.svelte';
import type { GameType, SavedGameType } from '$lib/types'; import type { GameType, SavedGameType } from '$lib/types';
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { wishlistStore } from '$root/lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import { addToCollection, removeFromCollection } from '$lib/util/manipulateCollection'; import { addToCollection, removeFromCollection } from '$lib/util/manipulateCollection';
import { addToWishlist } from '$lib/util/manipulateWishlist'; import { addToWishlist } from '$lib/util/manipulateWishlist';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { binarySearchOnStore } from '$root/lib/util/binarySearchOnStore'; import { binarySearchOnStore } from '$lib/util/binarySearchOnStore';
import { convertToSavedGame } from '$root/lib/util/gameMapper'; import { convertToSavedGame } from '$lib/util/gameMapper';
export let game: GameType | SavedGameType; export let game: GameType | SavedGameType;
export let detailed: boolean = false; export let detailed: boolean = false;

View file

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { collectionStore } from '$root/lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { ToastType } from '$root/lib/types'; import { ToastType } from '$lib/types';
import { SaveIcon, ShareIcon, TrashIcon } from '@rgossiaux/svelte-heroicons/outline'; import { SaveIcon, ShareIcon, TrashIcon } from '@rgossiaux/svelte-heroicons/outline';
import ClearCollectionDialog from '../dialog/ClearCollectionDialog.svelte'; import ClearCollectionDialog from '../dialog/ClearCollectionDialog.svelte';
import { toast } from '../toast/toast'; import { toast } from '../toast/toast';

View file

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { wishlistStore } from '$root/lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import { ToastType } from '$root/lib/types'; import { ToastType } from '$lib/types';
import { SaveIcon, ShareIcon, TrashIcon } from '@rgossiaux/svelte-heroicons/outline'; import { SaveIcon, ShareIcon, TrashIcon } from '@rgossiaux/svelte-heroicons/outline';
import ClearWishlistDialog from '../dialog/ClearWishlistDialog.svelte'; import ClearWishlistDialog from '../dialog/ClearWishlistDialog.svelte';
import { toast } from '../toast/toast'; import { toast } from '../toast/toast';

View file

@ -4,7 +4,7 @@
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { toast } from '$lib/components/toast/toast'; import { toast } from '$lib/components/toast/toast';
import { ToastType, type SavedGameType } from '$lib/types'; import { ToastType, type SavedGameType } from '$lib/types';
import { mapSavedGameToGame } from '$root/lib/util/gameMapper'; import { mapSavedGameToGame } from '$lib/util/gameMapper';
async function getRandomCollectionGame() { async function getRandomCollectionGame() {
if ($collectionStore.length > 0) { if ($collectionStore.length > 0) {

View file

@ -21,15 +21,15 @@
cancel(); cancel();
} }
}, },
onUpdated: ({ form }) => { // onUpdated: ({ form }) => {
if ($gameStore.length <= 0) { // if ($gameStore.length <= 0) {
toast.send('No results found 😿', { // toast.send('No results found 😿', {
duration: 3000, // duration: 3000,
type: ToastType.ERROR, // type: ToastType.ERROR,
dismissible: true // dismissible: true
}); // });
} // }
} // }
}); });
let submitting = $boredState?.loading; let submitting = $boredState?.loading;

View file

@ -10,11 +10,11 @@
import { boredState } from '$lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import AdvancedSearch from '$lib/components/search/advancedSearch/index.svelte'; import AdvancedSearch from '$lib/components/search/advancedSearch/index.svelte';
import { xl, md, sm } from '$lib/stores/mediaQueryStore'; import { xl, md, sm } from '$lib/stores/mediaQueryStore';
import { gameStore } from '$root/lib/stores/gameSearchStore'; import { gameStore } from '$lib/stores/gameSearchStore';
import { toast } from '../../toast/toast'; import { toast } from '../../toast/toast';
import Pagination from '$lib/components/pagination/index.svelte'; import Pagination from '$lib/components/pagination/index.svelte';
import Game from '$lib/components/game/index.svelte'; import Game from '$lib/components/game/index.svelte';
import { ToastType, type GameType, type SavedGameType } from '$root/lib/types'; import { ToastType, type GameType, type SavedGameType } from '$lib/types';
import SkeletonPlaceholder from '../../SkeletonPlaceholder.svelte'; import SkeletonPlaceholder from '../../SkeletonPlaceholder.svelte';
import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte'; import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte';
import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte'; import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte';

View file

@ -1,3 +1,4 @@
import { dev } from '$app/environment'; import { dev } from '$app/environment';
export const BASE_URL = dev ? 'http://localhost:5173' : 'https://boredgame.vercel.app'; export const BASE_URL = dev ? 'http://localhost:5173' : 'https://boredgame.vercel.app';
export const APP_NAME = 'Bored Game'; export const APP_NAME = 'Bored Game';
export const DOMAIN = 'boredgame.vercel.app';

70
src/lib/server/log.ts Normal file
View file

@ -0,0 +1,70 @@
import { Client } from '@axiomhq/axiom-node';
import { AXIOM_TOKEN, AXIOM_ORG_ID, AXIOM_DATASET } from '$env/static/private';
import getAllUrlParams from '$lib/util/getAllUrlParams';
import parseTrack from '$lib/util/parseTrack';
import parseMessage from '$lib/util/parseMessage';
import { DOMAIN } from '$lib/config/constants';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
export default async function log(statusCode: number, event) {
try {
let level = 'info';
if (statusCode >= 400) {
level = 'error';
}
const error = event?.locals?.error || undefined;
const errorId = event?.locals?.errorId || undefined;
const errorStackTrace = event?.locals?.errorStackTrace || undefined;
let urlParams = {};
if (event?.url?.search) {
urlParams = await getAllUrlParams(event?.url?.search);
}
let messageEvents = {};
if (event?.locals?.message) {
messageEvents = await parseMessage(event?.locals?.message);
}
let trackEvents = {};
if (event?.locals?.track) {
trackEvents = await parseTrack(event?.locals?.track);
}
let referer = event.request.headers.get('referer');
if (referer) {
const refererUrl = await new URL(referer);
const refererHostname = refererUrl.hostname;
if (refererHostname === 'localhost' || refererHostname === DOMAIN) {
referer = refererUrl.pathname;
}
} else {
referer = undefined;
}
const logData: object = {
level: level,
method: event.request.method,
path: event.url.pathname,
status: statusCode,
timeInMs: Date.now() - event?.locals?.startTimer,
user: event?.locals?.user?.email,
userId: event?.locals?.user?.userId,
referer: referer,
error: error,
errorId: errorId,
errorStackTrace: errorStackTrace,
...urlParams,
...messageEvents,
...trackEvents
};
console.log('log: ', JSON.stringify(logData));
if (!AXIOM_TOKEN || !AXIOM_ORG_ID || !AXIOM_DATASET) {
return;
}
const client = new Client({
token: AXIOM_TOKEN,
orgId: AXIOM_ORG_ID
});
await client.ingestEvents(AXIOM_DATASET, [logData]);
} catch (err) {
throw new Error(`Error Logger: ${JSON.stringify(err)}`);
}
}

View file

@ -0,0 +1,11 @@
export default async function getAllUrlParams(url: string): Promise<object> {
let paramsObj = {};
try {
url = url?.slice(1); // remove leading ?
if (!url) return {}; // if no params return
paramsObj = await Object.fromEntries(await new URLSearchParams(url));
} catch (error) {
console.log('error: ', error);
}
return paramsObj;
}

View file

@ -0,0 +1,15 @@
export default async function parseMessage(message: unknown): Promise<object> {
let messageObj = {};
try {
if (message) {
if (typeof message === 'string') {
messageObj = { message: message };
} else {
messageObj = message;
}
}
} catch (error) {
console.log('error: ', error);
}
return messageObj;
}

View file

@ -0,0 +1,15 @@
export default async function parseTrack(track: unknown): Promise<object> {
let trackObj = {};
try {
if (track) {
if (typeof track === 'string') {
trackObj = { track: track };
} else {
trackObj = track;
}
}
} catch (error) {
console.log('error: ', error);
}
return trackObj;
}

View file

@ -1,4 +1,20 @@
<h1>The page you requested doesn't exist! 🤷‍♂️</h1> <script lang="ts">
import { page } from '$app/stores';
</script>
<div>
{#if $page.status === 404}
<h1>The page you requested doesn't exist! 🤷‍♂️</h1>
<h3 class="mt-6"><a href="/">Go Home</a></h3>
{:else}
<h1 class="h1">Unexpected Error</h1>
<h3 class="mt-6">We're investigating the issue.</h3>
{/if}
{#if $page.error?.errorId}
<p class="mt-6">Error ID: {$page.error.errorId}</p>
{/if}
</div>
<style> <style>
h1 { h1 {

View file

@ -12,7 +12,7 @@
import Portal from '$lib/Portal.svelte'; import Portal from '$lib/Portal.svelte';
import { boredState } from '$lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { wishlistStore } from '$root/lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import { gameStore } from '$lib/stores/gameSearchStore'; import { gameStore } from '$lib/stores/gameSearchStore';
import { toast } from '$lib/components/toast/toast'; import { toast } from '$lib/components/toast/toast';
import Toast from '$lib/components/toast/Toast.svelte'; import Toast from '$lib/components/toast/Toast.svelte';

View file

@ -3,8 +3,10 @@ import { search_schema } from '$lib/zodValidation';
export const load = async ({ fetch, url }) => { export const load = async ({ fetch, url }) => {
const formData = Object.fromEntries(url?.searchParams); const formData = Object.fromEntries(url?.searchParams);
console.log('formData', formData);
formData.name = formData?.q; formData.name = formData?.q;
const form = await superValidate(formData, search_schema); const form = await superValidate(formData, search_schema);
console.log('form', form);
return { form }; return { form };
}; };

View file

@ -13,10 +13,10 @@ export const load = async (event) => {
if (session) { if (session) {
throw redirect(302, '/'); throw redirect(302, '/');
} }
const form = await superValidate(event, signInSchema); // const form = await superValidate(event, signInSchema);
return { // return {
form // form
}; // };
}; };
export const actions = { export const actions = {
@ -24,6 +24,7 @@ export const actions = {
const form = await superValidate(event, signInSchema); const form = await superValidate(event, signInSchema);
if (!form.valid) { if (!form.valid) {
form.data.password = '';
return fail(400, { return fail(400, {
form form
}); });
@ -31,16 +32,17 @@ export const actions = {
// Adding user to the db // Adding user to the db
try { try {
console.log('sign in user');
const key = await auth.useKey('username', form.data.username, form.data.password); const key = await auth.useKey('username', form.data.username, form.data.password);
const session = await auth.createSession(key.userId); const session = await auth.createSession(key.userId);
event.locals.auth.setSession(session); event.locals.auth.setSession(session);
} catch (e) { } catch (e) {
// TODO: need to return error message to the client // TODO: need to return error message to the client
console.error(e); console.error(e);
form.data.password = '';
return setError(form, null, 'The username or password is incorrect.'); return setError(form, null, 'The username or password is incorrect.');
} }
form.data.username = '';
form.data.password = '';
return { form }; return { form };
} }
}; };

View file

@ -7,6 +7,7 @@
const { form, errors, enhance, delayed } = superForm(data.form, { const { form, errors, enhance, delayed } = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: signInSchema, validators: signInSchema,
validationMethod: 'oninput',
delayMs: 0, delayMs: 0,
}); });
</script> </script>

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { userSchema } from '$root/lib/config/zod-schemas.js'; import { userSchema } from '$lib/config/zod-schemas.js';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
export let data; export let data;

View file

@ -2,12 +2,12 @@
import { tick, onDestroy } from 'svelte'; import { tick, onDestroy } from 'svelte';
import Game from '$lib/components/game/index.svelte'; import Game from '$lib/components/game/index.svelte';
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import type { GameType, SavedGameType } from '$root/lib/types'; import type { GameType, SavedGameType } from '$lib/types';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import Pagination from '$root/lib/components/pagination/index.svelte'; import Pagination from '$lib/components/pagination/index.svelte';
import RemoveCollectionDialog from '$root/lib/components/dialog/RemoveCollectionDialog.svelte'; import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte'; import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
import { createSearchStore, searchHandler } from '$root/lib/stores/search'; import { createSearchStore, searchHandler } from '$lib/stores/search';
import type { PageData } from './$types'; import type { PageData } from './$types';
export let data: PageData; export let data: PageData;

View file

@ -15,16 +15,16 @@
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { wishlistStore } from '$lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import Button from '$lib/components/button/index.svelte'; import Button from '$lib/components/button/index.svelte';
import RemoveCollectionDialog from '$root/lib/components/dialog/RemoveCollectionDialog.svelte'; import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
import { addToCollection } from '$lib/util/manipulateCollection'; import { addToCollection } from '$lib/util/manipulateCollection';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import LinkWithIcon from '$root/lib/components/LinkWithIcon.svelte'; import LinkWithIcon from '$lib/components/LinkWithIcon.svelte';
import { addToWishlist } from '$root/lib/util/manipulateWishlist'; import { addToWishlist } from '$lib/util/manipulateWishlist';
import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte'; import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
import { binarySearchOnStore } from '$root/lib/util/binarySearchOnStore'; import { binarySearchOnStore } from '$lib/util/binarySearchOnStore';
import { convertToSavedGame } from '$root/lib/util/gameMapper'; import { convertToSavedGame } from '$lib/util/gameMapper';
$: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id); $: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id);
$: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id); $: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id);

View file

@ -2,12 +2,12 @@
import { tick, onDestroy } from 'svelte'; import { tick, onDestroy } from 'svelte';
import Game from '$lib/components/game/index.svelte'; import Game from '$lib/components/game/index.svelte';
import { wishlistStore } from '$lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import type { GameType, SavedGameType } from '$root/lib/types'; import type { GameType, SavedGameType } from '$lib/types';
import { boredState } from '$root/lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import Pagination from '$root/lib/components/pagination/index.svelte'; import Pagination from '$lib/components/pagination/index.svelte';
import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte'; import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
import RemoveCollectionDialog from '$root/lib/components/dialog/RemoveCollectionDialog.svelte'; import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
import { createSearchStore, searchHandler } from '$root/lib/stores/search'; import { createSearchStore, searchHandler } from '$lib/stores/search';
let gameToRemove: GameType | SavedGameType; let gameToRemove: GameType | SavedGameType;
let pageSize = 10; let pageSize = 10;

View file

@ -1,181 +1,181 @@
import { invalid, type RequestEvent } from '@sveltejs/kit'; import { invalid, type RequestEvent } from '@sveltejs/kit';
import { BOARD_GAME_ATLAS_CLIENT_ID } from '$env/static/private'; import { BOARD_GAME_ATLAS_CLIENT_ID } from '$env/static/private';
import type { GameType, SearchQuery } from "$root/lib/types"; import type { GameType, SearchQuery } from '$lib/types';
import { mapAPIGameToBoredGame } from "$root/lib/util/gameMapper"; import { mapAPIGameToBoredGame } from '$lib/util/gameMapper';
interface Actions { interface Actions {
[key: string]: any // Action [key: string]: any; // Action
} }
export const Games: Actions = { export const Games: Actions = {
search: async ({ request, locals }: RequestEvent): Promise<any> => { search: async ({ request, locals }: RequestEvent): Promise<any> => {
console.log("In search action specific") console.log('In search action specific');
// Do things in here // Do things in here
const form = await request.formData(); const form = await request.formData();
console.log('action form', form); console.log('action form', form);
const queryParams: SearchQuery = { const queryParams: SearchQuery = {
order_by: 'rank', order_by: 'rank',
ascending: false, ascending: false,
limit: 10, limit: 10,
skip: 0, skip: 0,
client_id: BOARD_GAME_ATLAS_CLIENT_ID, client_id: BOARD_GAME_ATLAS_CLIENT_ID,
fuzzy_match: true, fuzzy_match: true,
name: '' name: ''
}; };
const name = form.has('name') ? form.get('name') : await request?.text(); const name = form.has('name') ? form.get('name') : await request?.text();
console.log('name', name); console.log('name', name);
if (name) { if (name) {
queryParams.name = `${name}`; queryParams.name = `${name}`;
} }
const newQueryParams: Record<string, string> = {}; const newQueryParams: Record<string, string> = {};
for (const key in queryParams) { for (const key in queryParams) {
console.log('key', key); console.log('key', key);
console.log('queryParams[key]', queryParams[key]); console.log('queryParams[key]', queryParams[key]);
newQueryParams[key] = `${queryParams[key]}`; newQueryParams[key] = `${queryParams[key]}`;
} }
const urlQueryParams = new URLSearchParams(newQueryParams); const urlQueryParams = new URLSearchParams(newQueryParams);
console.log('urlQueryParams', urlQueryParams); console.log('urlQueryParams', urlQueryParams);
try { try {
throw new Error("test error"); throw new Error('test error');
// const url = `https://api.boardgameatlas.com/api/search${urlQueryParams ? `?${urlQueryParams}` : '' // const url = `https://api.boardgameatlas.com/api/search${urlQueryParams ? `?${urlQueryParams}` : ''
// }`; // }`;
// const response = await fetch(url, { // const response = await fetch(url, {
// method: 'get', // method: 'get',
// headers: { // headers: {
// 'content-type': 'application/json' // 'content-type': 'application/json'
// } // }
// }); // });
// console.log('board game response', response); // console.log('board game response', response);
// if (response.status !== 200) { // if (response.status !== 200) {
// console.log('Status not 200', response.status) // console.log('Status not 200', response.status)
// invalid(response.status, {}); // invalid(response.status, {});
// } // }
// if (response.status === 200) { // if (response.status === 200) {
// const gameResponse = await response.json(); // const gameResponse = await response.json();
// console.log('gameResponse', gameResponse); // console.log('gameResponse', gameResponse);
// const gameList = gameResponse?.games; // const gameList = gameResponse?.games;
// const totalCount = gameResponse?.count; // const totalCount = gameResponse?.count;
// console.log('totalCount', totalCount); // console.log('totalCount', totalCount);
// const games: GameType[] = []; // const games: GameType[] = [];
// gameList.forEach((game) => { // gameList.forEach((game) => {
// games.push(mapAPIGameToBoredGame(game)); // games.push(mapAPIGameToBoredGame(game));
// }); // });
// console.log('returning from search') // console.log('returning from search')
// return { // return {
// games, // games,
// totalCount: games.length // totalCount: games.length
// }; // };
// } // }
// return { // return {
// games: [], // games: [],
// totalCount: 0 // totalCount: 0
// }; // };
} catch (e) { } catch (e) {
console.log(`Error searching board games ${e}`); console.log(`Error searching board games ${e}`);
return invalid(400, { reason: 'Exception' }) return invalid(400, { reason: 'Exception' });
} }
} }
// const id = form.get('id'); // const id = form.get('id');
// const ids = form.get('ids'); // const ids = form.get('ids');
// const minAge = form.get('minAge'); // const minAge = form.get('minAge');
// const minPlayers = form.get('minPlayers'); // const minPlayers = form.get('minPlayers');
// const maxPlayers = form.get('maxPlayers'); // const maxPlayers = form.get('maxPlayers');
// const exactMinAge = form.get('exactMinAge') || false; // const exactMinAge = form.get('exactMinAge') || false;
// const exactMinPlayers = form.get('exactMinPlayers') || false; // const exactMinPlayers = form.get('exactMinPlayers') || false;
// const exactMaxPlayers = form.get('exactMaxPlayers') || false; // const exactMaxPlayers = form.get('exactMaxPlayers') || false;
// const random = form.get('random') === 'on' || false; // const random = form.get('random') === 'on' || false;
// if (minAge) { // if (minAge) {
// if (exactMinAge) { // if (exactMinAge) {
// queryParams.min_age = +minAge; // queryParams.min_age = +minAge;
// } else { // } else {
// queryParams.gt_min_age = +minAge === 1 ? 0 : +minAge - 1; // queryParams.gt_min_age = +minAge === 1 ? 0 : +minAge - 1;
// } // }
// } // }
// if (minPlayers) { // if (minPlayers) {
// if (exactMinPlayers) { // if (exactMinPlayers) {
// queryParams.min_players = +minPlayers; // queryParams.min_players = +minPlayers;
// } else { // } else {
// queryParams.gt_min_players = +minPlayers === 1 ? 0 : +minPlayers - 1; // queryParams.gt_min_players = +minPlayers === 1 ? 0 : +minPlayers - 1;
// } // }
// } // }
// if (maxPlayers) { // if (maxPlayers) {
// if (exactMaxPlayers) { // if (exactMaxPlayers) {
// queryParams.max_players = +maxPlayers; // queryParams.max_players = +maxPlayers;
// } else { // } else {
// queryParams.lt_max_players = +maxPlayers + 1; // queryParams.lt_max_players = +maxPlayers + 1;
// } // }
// } // }
// if (id) { // if (id) {
// queryParams.ids = new Array(`${id}`); // queryParams.ids = new Array(`${id}`);
// } // }
// if (ids) { // if (ids) {
// // TODO: Pass in ids array from localstorage / game store // // TODO: Pass in ids array from localstorage / game store
// queryParams.ids = new Array(ids); // queryParams.ids = new Array(ids);
// } // }
// queryParams.random = random; // queryParams.random = random;
// console.log('queryParams', queryParams); // console.log('queryParams', queryParams);
// const newQueryParams: Record<string, string> = {}; // const newQueryParams: Record<string, string> = {};
// for (const key in queryParams) { // for (const key in queryParams) {
// newQueryParams[key] = `${queryParams[key as keyof typeof queryParams]}`; // newQueryParams[key] = `${queryParams[key as keyof typeof queryParams]}`;
// } // }
// const urlQueryParams = new URLSearchParams(newQueryParams); // const urlQueryParams = new URLSearchParams(newQueryParams);
// const url = `https://api.boardgameatlas.com/api/search${urlQueryParams ? `?${urlQueryParams}` : '' // const url = `https://api.boardgameatlas.com/api/search${urlQueryParams ? `?${urlQueryParams}` : ''
// }`; // }`;
// const response = await fetch(url, { // const response = await fetch(url, {
// method: 'get', // method: 'get',
// headers: { // headers: {
// 'content-type': 'application/json' // 'content-type': 'application/json'
// } // }
// }); // });
// console.log('response status', response.status); // console.log('response status', response.status);
// console.log('board game response action', response); // console.log('board game response action', response);
// if (response.status === 404) { // if (response.status === 404) {
// // user hasn't created a todo list. // // user hasn't created a todo list.
// // start with an empty array // // start with an empty array
// return { // return {
// success: true, // success: true,
// games: [], // games: [],
// totalCount: 0 // totalCount: 0
// }; // };
// } // }
// if (response.status === 200) { // if (response.status === 200) {
// const gameResponse = await response.json(); // const gameResponse = await response.json();
// console.log('gameResponse', gameResponse); // console.log('gameResponse', gameResponse);
// const gameList = gameResponse?.games; // const gameList = gameResponse?.games;
// const games: GameType[] = []; // const games: GameType[] = [];
// gameList.forEach((game: GameType) => { // gameList.forEach((game: GameType) => {
// games.push(mapAPIGameToBoredGame(game)); // games.push(mapAPIGameToBoredGame(game));
// }); // });
// console.log('action games', games); // console.log('action games', games);
// return { // return {
// games, // games,
// totalCount: games.length // totalCount: games.length
// }; // };
// } // }
// return { success: false }; // return { success: false };
// } // }
// create: async function create({ request, locals }): Promise<any> { // create: async function create({ request, locals }): Promise<any> {
// const data = await getFormDataObject<any>(request); // const data = await getFormDataObject<any>(request);
// return data; // return data;
// } // }
} };

View file

@ -1,3 +1,3 @@
@import 'reset.pcss'; @import 'reset.pcss';
@import 'global.pcss'; @import 'global.pcss';
@import '$root/styles/theme.pcss'; @import 'theme.pcss';

View file

@ -14,20 +14,16 @@ const config = {
postcss: true postcss: true
}) })
], ],
vitePlugin: {
inspector: true,
},
kit: { kit: {
adapter: adapter(), adapter: adapter(),
alias: { alias: {
$root: './src', $lib: './src/lib',
$styles: './src/styles', $styles: './src/styles',
} }
}, },
vitePlugin: {
experimental: {
inspector: {
toggleKeyCombo: 'control-alt-shift'
}
}
}
}; };
export default config; export default config;