import { json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { db } from '$lib/server/db'; import { dailyCompletions } from '$lib/server/db/schema'; import { eq, and } from 'drizzle-orm'; import { scheduleSubmission, validateSelection, getCooldownState } from '$lib/server/verse-submission'; /** * POST /api/submit-verse * * Accepts a user-chosen verse (bookId/chapter/verse) and reserves a concrete * future date for it via the scheduling scan. Auth required, solved-today gate, * 7-day rolling cooldown, and structural validation all enforced here. * * Body: { bookId, chapter, verse, localDate } */ export const POST: RequestHandler = async ({ request, locals }) => { // 1. Auth. if (!locals.user) { return json({ error: 'Unauthorized' }, { status: 401 }); } const userId = locals.user.id; let body: any; try { body = await request.json(); } catch { return json({ error: 'Invalid JSON body' }, { status: 400 }); } const { bookId, chapter, verse, localDate } = body ?? {}; // 2. Solved-today gate (engagement gate, not security). if (typeof localDate !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(localDate)) { return json({ error: 'A valid localDate (YYYY-MM-DD) is required' }, { status: 400 }); } const [completion] = await db .select({ id: dailyCompletions.id }) .from(dailyCompletions) .where( and( eq(dailyCompletions.anonymousId, userId), eq(dailyCompletions.date, localDate) ) ) .limit(1); if (!completion) { return json({ error: "Solve today's puzzle first" }, { status: 403 }); } // 3. Cooldown (rolling 7×24h, server UTC). const cooldown = await getCooldownState(db, userId); if (cooldown.onCooldown) { return json( { error: 'Cooldown active', cooldownEndsAt: cooldown.cooldownEndsAt }, { status: 429 } ); } // 4. Structural validation. const validationError = validateSelection({ bookId, chapter, verse }); if (validationError) { return json({ error: validationError }, { status: 400 }); } // 5–7. Compute window + scheduling scan + transactional insert (with retry). try { const result = await scheduleSubmission( db, { bookId, chapter, verse }, userId ); return json( { scheduledDate: result.scheduledDate, reference: result.reference, windowText: result.windowText }, { status: 201 } ); } catch (err) { console.error('submit-verse failed:', err); return json({ error: 'Failed to schedule submission' }, { status: 500 }); } };