switched to bun:sqlite

This commit is contained in:
George Powell
2026-02-05 00:47:55 -05:00
parent dfe784b744
commit dfe1c40a8a
10 changed files with 223 additions and 17 deletions

View File

@@ -11,7 +11,9 @@
import Credits from "$lib/components/Credits.svelte";
import TitleAnimation from "$lib/components/TitleAnimation.svelte";
import DevButtons from "$lib/components/DevButtons.svelte";
import AuthModal from "$lib/components/AuthModal.svelte";
import { getGrade } from "$lib/utils/game";
import { enhance } from '$app/forms';
interface Guess {
book: BibleBook;
@@ -25,6 +27,8 @@
let dailyVerse = $derived(data.dailyVerse);
let correctBookId = $derived(data.correctBookId);
let user = $derived(data.user);
let session = $derived(data.session);
let guesses = $state<Guess[]>([]);
@@ -37,6 +41,7 @@
let anonymousId = $state("");
let statsSubmitted = $state(false);
let authModalOpen = $state(false);
let statsData = $state<{
solveRank: number;
guessRank: number;
@@ -447,13 +452,42 @@
<span class="big-text"
>{isDev ? "Dev Edition | " : ""}{currentDate}</span
>
<div class="mt-4">
<a
href="/stats?anonymousId={anonymousId}"
class="inline-flex items-center px-4 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors text-sm font-medium shadow-md"
>
📊 View Stats
</a>
<div class="mt-4 flex flex-col items-center gap-3">
<div class="flex gap-3">
<a
href="/stats?anonymousId={anonymousId}"
class="inline-flex items-center px-4 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors text-sm font-medium shadow-md"
>
📊 View Stats
</a>
{#if user}
<form method="POST" action="/auth/logout" use:enhance>
<button
type="submit"
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium shadow-md"
>
🚪 Sign Out
</button>
</form>
{:else}
<button
onclick={() => authModalOpen = true}
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium shadow-md"
>
🔐 Sign In
</button>
{/if}
</div>
{#if isDev}
<div class="text-xs text-gray-600 bg-gray-100 px-3 py-2 rounded border">
<div><strong>Debug Info:</strong></div>
<div>User: {user ? `${user.email} (ID: ${user.id})` : 'Not signed in'}</div>
<div>Session: {session ? `Expires ${session.expiresAt.toLocaleDateString()}` : 'No session'}</div>
<div>Anonymous ID: {anonymousId || 'Not set'}</div>
</div>
{/if}
</div>
</div>
@@ -502,3 +536,5 @@
{/if}
</div>
</div>
<AuthModal bind:isOpen={authModalOpen} {anonymousId} />

View File

@@ -0,0 +1,13 @@
import { redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import * as auth from '$lib/server/auth';
export const actions: Actions = {
default: async ({ locals, cookies }) => {
if (locals.session) {
await auth.invalidateSession(locals.session.id);
}
auth.deleteSessionTokenCookie({ cookies });
redirect(302, '/');
}
};

View File

@@ -0,0 +1,49 @@
import { redirect, fail } from '@sveltejs/kit';
import type { Actions } from './$types';
import * as auth from '$lib/server/auth';
export const actions: Actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email')?.toString();
const password = data.get('password')?.toString();
if (!email || !password) {
return fail(400, { error: 'Email and password are required' });
}
// Basic email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return fail(400, { error: 'Please enter a valid email address' });
}
if (password.length < 6) {
return fail(400, { error: 'Password must be at least 6 characters' });
}
try {
// Get user by email
const user = await auth.getUserByEmail(email);
if (!user || !user.passwordHash) {
return fail(400, { error: 'Invalid email or password' });
}
// Verify password
const isValidPassword = await auth.verifyPassword(password, user.passwordHash);
if (!isValidPassword) {
return fail(400, { error: 'Invalid email or password' });
}
// Create session
const sessionToken = auth.generateSessionToken();
const session = await auth.createSession(sessionToken, user.id);
auth.setSessionTokenCookie({ cookies }, sessionToken, session.expiresAt);
return { success: true };
} catch (error) {
console.error('Sign in error:', error);
return fail(500, { error: 'An error occurred during sign in' });
}
}
};

View File

@@ -0,0 +1,64 @@
import { redirect, fail } from '@sveltejs/kit';
import type { Actions } from './$types';
import * as auth from '$lib/server/auth';
export const actions: Actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email')?.toString();
const password = data.get('password')?.toString();
const firstName = data.get('firstName')?.toString();
const lastName = data.get('lastName')?.toString();
const anonymousId = data.get('anonymousId')?.toString();
if (!email || !password || !anonymousId) {
return fail(400, { error: 'Email, password, and anonymous ID are required' });
}
// Basic email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return fail(400, { error: 'Please enter a valid email address' });
}
if (password.length < 6) {
return fail(400, { error: 'Password must be at least 6 characters' });
}
try {
// Check if user already exists
const existingUser = await auth.getUserByEmail(email);
if (existingUser) {
return fail(400, { error: 'An account with this email already exists' });
}
// Hash password
const passwordHash = await auth.hashPassword(password);
// Create user with anonymousId as the user ID
const user = await auth.createUser(
anonymousId,
email,
passwordHash,
firstName || undefined,
lastName || undefined
);
// Create session
const sessionToken = auth.generateSessionToken();
const session = await auth.createSession(sessionToken, user.id);
auth.setSessionTokenCookie({ cookies }, sessionToken, session.expiresAt);
return { success: true };
} catch (error) {
console.error('Sign up error:', error);
// Check if it's a unique constraint error (user with this ID already exists)
if (error instanceof Error && error.message.includes('UNIQUE constraint')) {
return fail(400, { error: 'This account is already registered. Please sign in instead.' });
}
return fail(500, { error: 'An error occurred during account creation' });
}
}
};