Feat: Genre system with per-genre daily puzzles

This commit is contained in:
Hördle Bot
2025-11-22 11:56:16 +01:00
parent dc69fd1498
commit 8c720e287f
9 changed files with 294 additions and 155 deletions

View File

@@ -1,81 +1,29 @@
import Game from '@/components/Game';
import { getTodayISOString } from '@/lib/dateUtils';
import { getOrCreateDailyPuzzle } from '@/lib/dailyPuzzle';
import Link from 'next/link';
import { PrismaClient } from '@prisma/client';
export const dynamic = 'force-dynamic';
import { PrismaClient } from '@prisma/client';
// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
const globalForPrisma = global as unknown as { prisma: PrismaClient };
const prisma = globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
async function getDailyPuzzle() {
try {
const today = getTodayISOString();
console.log(`[getDailyPuzzle] Checking puzzle for date: ${today}`);
let dailyPuzzle = await prisma.dailyPuzzle.findUnique({
where: { date: today },
include: { song: true },
});
if (!dailyPuzzle) {
console.log('[getDailyPuzzle] No puzzle found, attempting to create...');
const songsCount = await prisma.song.count();
console.log(`[getDailyPuzzle] Found ${songsCount} songs in DB`);
if (songsCount > 0) {
const skip = Math.floor(Math.random() * songsCount);
const randomSong = await prisma.song.findFirst({ skip });
if (randomSong) {
try {
dailyPuzzle = await prisma.dailyPuzzle.create({
data: { date: today, songId: randomSong.id },
include: { song: true },
});
console.log(`[getDailyPuzzle] Created puzzle for song: ${randomSong.title}`);
} catch (createError) {
// Handle race condition: if another request created it in the meantime
console.log('[getDailyPuzzle] Creation failed, trying to fetch again (likely race condition)');
dailyPuzzle = await prisma.dailyPuzzle.findUnique({
where: { date: today },
include: { song: true },
});
}
}
} else {
console.log('[getDailyPuzzle] No songs available to create puzzle');
}
}
if (!dailyPuzzle) {
console.log('[getDailyPuzzle] Failed to get or create puzzle');
return null;
}
return {
id: dailyPuzzle.id,
audioUrl: `/uploads/${dailyPuzzle.song.filename}`,
songId: dailyPuzzle.songId,
title: dailyPuzzle.song.title,
artist: dailyPuzzle.song.artist,
coverImage: dailyPuzzle.song.coverImage ? `/uploads/covers/${dailyPuzzle.song.coverImage}` : null,
};
} catch (e) {
console.error('[getDailyPuzzle] Error:', e);
return null;
}
}
const prisma = new PrismaClient();
export default async function Home() {
const dailyPuzzle = await getDailyPuzzle();
const dailyPuzzle = await getOrCreateDailyPuzzle(null); // Global puzzle
const genres = await prisma.genre.findMany({ orderBy: { name: 'asc' } });
return (
<Game dailyPuzzle={dailyPuzzle} />
<>
<div style={{ textAlign: 'center', padding: '1rem', background: '#f3f4f6' }}>
<div style={{ display: 'flex', justifyContent: 'center', gap: '1rem', flexWrap: 'wrap' }}>
<Link href="/" style={{ fontWeight: 'bold', textDecoration: 'underline' }}>Global</Link>
{genres.map(g => (
<Link key={g.id} href={`/${g.name}`} style={{ color: '#4b5563', textDecoration: 'none' }}>
{g.name}
</Link>
))}
</div>
</div>
<Game dailyPuzzle={dailyPuzzle} genre={null} />
</>
);
}