style changes and stuff

This commit is contained in:
2026-07-05 13:46:16 +01:00
parent 07c27ffaaa
commit 4df2bc697d
5 changed files with 56 additions and 27 deletions

View File

@@ -50,7 +50,8 @@ async function seed() {
console.log('Database reset complete. Seeding...');
let passwordHash = await Bun.password.hash('password');
const { hash } = await import('../src/lib/server/password.js');
let passwordHash = await hash('password');
await db
.insert(schema.scorers)
.values({

View File

@@ -0,0 +1,24 @@
import { scrypt, randomBytes, timingSafeEqual } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
const SALT_LEN = 32;
const KEY_LEN = 64;
export async function hash(password: string): Promise<string> {
const salt = randomBytes(SALT_LEN);
const derivedKey = (await scryptAsync(password, salt, KEY_LEN)) as Buffer;
return `${salt.toString('hex')}:${derivedKey.toString('hex')}`;
}
export async function verify(password: string, stored: string): Promise<boolean> {
const [saltHex, keyHex] = stored.split(':');
if (!saltHex || !keyHex) return false;
const salt = Buffer.from(saltHex, 'hex');
const key = Buffer.from(keyHex, 'hex');
const derivedKey = (await scryptAsync(password, salt, KEY_LEN)) as Buffer;
if (key.length !== derivedKey.length) return false;
return timingSafeEqual(key, derivedKey);
}

View File

@@ -101,6 +101,10 @@
align-items: center;
padding: 0 14px;
min-width: 0;
gap: 8px;
}
.runner .score-fg {
padding: 0 8px;
}
.score-meta {
display: flex;
@@ -120,20 +124,13 @@
text-shadow: 0 0 6px rgb(0 0 0 / 50%);
}
.score-name {
font-size: clamp(20px, 5.5vw, 40px);
font-size: clamp(18px, 3.5vw, 22px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.score-pts {
font-size: clamp(20px, 5.5vw, 40px);
letter-spacing: 2px;
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
z-index: 2;
flex-shrink: 0;
text-shadow: 0 0 8px rgb(0 0 0 / 80%), 0 0 4px rgb(0 0 0 / 60%);
}
.winner .score-meta {
@@ -147,7 +144,6 @@
}
.winner .score-name {
font-size: clamp(34px, 9vw, 64px);
max-width: 70%;
}
.winner .score-pts {
font-size: clamp(34px, 9vw, 64px);
@@ -163,42 +159,52 @@
.score-rank {
margin-bottom: 0;
}
.score-name {
font-size: clamp(18px, 5vw, 28px);
.runner .score-rank {
font-size: clamp(16px, 3.5vw, 20px);
}
.runner .score-name {
font-size: clamp(20px, 5vw, 26px);
}
.score-pts {
font-size: clamp(22px, 6vw, 34px);
}
.winner .score-name {
font-size: clamp(26px, 7vw, 42px);
max-width: 60%;
}
.winner .score-pts {
font-size: clamp(28px, 8vw, 48px);
}
}
/* Responsive runners-up: 1 col <480px, 2 cols 480-699px, 4 cols ≥700px */
/* Responsive runners-up: 1 col <480px, 2 cols 480-950px, 4 cols >950px */
.runners-grid {
display: grid;
gap: 10px;
grid-template-columns: 1fr;
transition: gap 0.3s ease;
}
@media (min-width: 480px) {
@media (min-width: 480px) and (max-width: 950px) {
.runners-grid {
grid-template-columns: repeat(2, 1fr);
}
.winner {
aspect-ratio: 2 / 1;
aspect-ratio: 12 / 5;
}
.runner {
aspect-ratio: 2 / 1;
}
.runner .score-rank {
font-size: clamp(18px, 3vw, 22px);
}
@media (min-width: 700px) {
.runner .score-name {
font-size: clamp(24px, 4vw, 30px);
}
}
@media (min-width: 951px) {
.runners-grid {
/* Use --runner-count so fewer teams fill the row evenly */
grid-template-columns: repeat(min(var(--runner-count), 4), 1fr);
@@ -378,7 +384,7 @@
}
}
@media (min-width: 700px) {
@media (min-width: 951px) {
.player-box {
max-width: calc(25% - 6px);
}

View File

@@ -1,12 +1,10 @@
// src/routes/login/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { db } from '$lib/server/db';
import { scorers } from '$lib/server/db/schema';
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
import type { Actions } from '../login/$types';
import type { PageServerLoad } from '../login/$types';
import { verify } from '$lib/server/password';
import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
return { user: locals.user };
@@ -19,7 +17,7 @@ export const actions: Actions = {
const password = data.get('password') as string;
const [user] = await db.select().from(scorers).where(eq(scorers.username, username));
if (!user || !(await Bun.password.verify(password, user.passwordHash))) {
if (!user || !(await verify(password, user.passwordHash))) {
return fail(400, { message: 'Invalid credentials' });
}

View File

@@ -1,11 +1,11 @@
// src/routes/signup/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { db } from '$lib/server/db';
import { scorers } from '$lib/server/db/schema';
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
import { hash } from '$lib/server/password';
import { getSetting } from '$lib/server/databaseManager';
import type { PageServerLoad, Actions } from '../signup/$types';
import type { PageServerLoad, Actions } from './$types';
export const load: PageServerLoad = async () => {
const disabled = (await getSetting('disableRegistration')) === 'true';
@@ -32,7 +32,7 @@ export const actions: Actions = {
return fail(400, { message: 'Username already taken' });
}
const passwordHash = await Bun.password.hash(password);
const passwordHash = await hash(password);
const userId = crypto.randomUUID();
await db.insert(scorers).values({ id: userId, username, passwordHash });