Files
website/src/routes/projects/catch-em-all/+page.server.ts
Stan Runge d0c7b6548b
Some checks failed
Test and deploy staging / staging (push) Has been cancelled
beeeeg update
2026-09-23 04:16:50 +00:00

139 lines
4.2 KiB
TypeScript

import { db } from '$lib/server/db';
import {
games,
pokemonGameEntries,
userEntries,
userGames,
catchEmAllSettings
} from '$lib/server/db/schema';
import { and, eq, inArray } from 'drizzle-orm';
import type { Actions, PageServerLoad } from './$types';
import { availabilityValues } from '$lib/availability';
import { fail } from '@sveltejs/kit';
export type WireEntry = [id: number, gameId: number, availability: number, note?: string];
export const load: PageServerLoad = async ({ locals }) => {
const pokemonRows = await db.query.pokemon.findMany({
with: {
entry: true
},
orderBy: { number: 'asc' }
});
const pokemon = pokemonRows.map(({ entry, ...rest }) => ({
...rest,
entries: entry.map((e): WireEntry => {
const code = availabilityValues.indexOf(e.availability);
return e.note === null ? [e.id, e.gameId, code] : [e.id, e.gameId, code, e.note];
})
}));
const gameRows = await db.query.games.findMany({
orderBy: { generation: 'asc', id: 'asc' }
});
let caughtIds: number[] = [];
let selectedGameIds: number[] = [];
let gamesConfigured = false;
if (locals.user) {
const [settings] = await db
.select()
.from(catchEmAllSettings)
.where(eq(catchEmAllSettings.userId, locals.user.id));
gamesConfigured = !!settings;
const caughtRows = await db
.select({ id: userEntries.pokemonGameEntryId })
.from(userEntries)
.where(eq(userEntries.userId, locals.user.id));
caughtIds = caughtRows.map((r) => r.id);
const gameRows = await db
.select({ id: userGames.gameId })
.from(userGames)
.where(eq(userGames.userId, locals.user.id));
selectedGameIds = gameRows.map((r) => r.id);
}
return {
pokemon,
games: gameRows,
caughtIds,
selectedGameIds,
gamesConfigured: gamesConfigured || selectedGameIds.length > 0
};
};
export const actions = {
togglePokemonCaught: async ({ request, locals }) => {
if (!locals.user) return fail(401, { message: 'Log in to track your progress' });
const data = await request.formData();
const entryId = Number(data.get('entryId'));
if (!Number.isSafeInteger(entryId) || entryId <= 0 || entryId > 2147483647)
return fail(400, { message: 'Invalid entry' });
// only catchable entries can be toggled — the client disables these
// checkboxes but we can't trust that
const [entry] = await db
.select({ availability: pokemonGameEntries.availability })
.from(pokemonGameEntries)
.where(eq(pokemonGameEntries.id, entryId));
if (!entry) return fail(404, { message: 'Unknown entry' });
if (entry.availability !== 'catchable') {
return fail(400, { message: 'This entry does not have confirmed encounter data' });
}
if (data.has('caught') && data.get('caught') !== 'on')
return fail(400, { message: 'Invalid caught state' });
const caught = data.get('caught') === 'on';
if (caught) {
await db
.insert(userEntries)
.values({ pokemonGameEntryId: entryId, userId: locals.user.id })
.onConflictDoNothing();
} else {
await db
.delete(userEntries)
.where(
and(eq(userEntries.pokemonGameEntryId, entryId), eq(userEntries.userId, locals.user.id))
);
}
return { saved: true };
},
setGames: async ({ request, locals }) => {
if (!locals.user) return fail(401, { message: 'Log in to set rules' });
const data = await request.formData();
const submittedIds = data.getAll('gameIds').map(Number);
if (
submittedIds.length > 200 ||
submittedIds.some((id) => !Number.isSafeInteger(id) || id <= 0)
) {
return fail(400, { message: 'Invalid game selection' });
}
const gameIds = [...new Set(submittedIds)];
if (gameIds.length > 0) {
const validGames = await db
.select({ id: games.id })
.from(games)
.where(inArray(games.id, gameIds));
if (validGames.length !== gameIds.length) return fail(400, { message: 'Unknown game' });
}
const userId = locals.user.id;
await db.transaction(async (tx) => {
await tx
.insert(catchEmAllSettings)
.values({ userId })
.onConflictDoUpdate({ target: catchEmAllSettings.userId, set: { userId } });
await tx.delete(userGames).where(eq(userGames.userId, userId));
if (gameIds.length > 0) {
await tx.insert(userGames).values(gameIds.map((gameId) => ({ userId, gameId })));
}
});
return { saved: true };
}
} satisfies Actions;