2026-04-05 00:55:53 -04:00

172 lines
4.5 KiB
TypeScript

import { NextResponse } from "next/server";
import { db } from "@/db";
import { quizSessions } from "@/db/schema";
import { eq, and, desc } from "drizzle-orm";
import { getSession } from "@/lib/auth";
import { WARMUP_PARAGRAPHS } from "@/lib/warmup-paragraphs";
/**
* Warmup progress is keyed by the auth session's createdAt timestamp.
* This means warmup resets every time the user logs in (new session),
* while the quiz pass is permanent.
*/
interface WarmupProgressMap {
[sessionKey: string]: number;
}
function getWarmupCompleted(
answersJson: string,
sessionKey: string,
): number {
try {
const parsed = JSON.parse(answersJson);
if (parsed && typeof parsed === "object" && "warmupBySession" in parsed) {
return (parsed.warmupBySession as WarmupProgressMap)[sessionKey] ?? 0;
}
// Legacy format: single warmupCompleted
if (parsed && typeof parsed === "object" && "warmupCompleted" in parsed) {
return 0; // Reset legacy progress — they need to redo warmup
}
return 0;
} catch {
return 0;
}
}
async function getPassedQuizSession(annotatorId: string) {
const [session] = await db
.select()
.from(quizSessions)
.where(
and(
eq(quizSessions.annotatorId, annotatorId),
eq(quizSessions.passed, true),
),
)
.orderBy(desc(quizSessions.startedAt))
.limit(1);
return session ?? null;
}
export async function GET() {
const session = await getSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const quizSession = await getPassedQuizSession(session.annotatorId);
if (!quizSession) {
return NextResponse.json(
{ error: "Quiz not passed" },
{ status: 403 },
);
}
const sessionKey = String(session.createdAt);
const warmupCompleted = getWarmupCompleted(quizSession.answers, sessionKey);
if (warmupCompleted >= WARMUP_PARAGRAPHS.length) {
return NextResponse.json({ done: true, warmupCompleted });
}
const next = WARMUP_PARAGRAPHS[warmupCompleted];
return NextResponse.json({
done: false,
warmupCompleted,
paragraph: {
id: next.id,
text: next.text,
index: warmupCompleted,
total: WARMUP_PARAGRAPHS.length,
},
});
}
export async function POST(request: Request) {
const session = await getSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const quizSession = await getPassedQuizSession(session.annotatorId);
if (!quizSession) {
return NextResponse.json(
{ error: "Quiz not passed" },
{ status: 403 },
);
}
const body = await request.json();
const { category, specificity, warmupIndex } = body as {
category?: string;
specificity?: number;
warmupIndex?: number;
};
if (!category || !specificity || warmupIndex === undefined) {
return NextResponse.json(
{ error: "Missing required fields: category, specificity, warmupIndex" },
{ status: 400 },
);
}
const sessionKey = String(session.createdAt);
const warmupCompleted = getWarmupCompleted(quizSession.answers, sessionKey);
if (warmupIndex !== warmupCompleted) {
return NextResponse.json(
{ error: "Warmup index mismatch" },
{ status: 400 },
);
}
if (warmupIndex >= WARMUP_PARAGRAPHS.length) {
return NextResponse.json(
{ error: "Warmup already complete" },
{ status: 400 },
);
}
const gold = WARMUP_PARAGRAPHS[warmupIndex];
const newCompleted = warmupIndex + 1;
const categoryCorrect = category === gold.goldCategory;
const specificityCorrect = specificity === gold.goldSpecificity;
// Store warmup progress keyed by auth session
let existingData: Record<string, unknown>;
try {
const parsed = JSON.parse(quizSession.answers);
if (Array.isArray(parsed)) {
existingData = { quizAnswers: parsed };
} else {
existingData = parsed;
}
} catch {
existingData = {};
}
const warmupBySession = (existingData.warmupBySession as WarmupProgressMap) ?? {};
warmupBySession[sessionKey] = newCompleted;
await db
.update(quizSessions)
.set({
answers: JSON.stringify({
...existingData,
warmupBySession,
}),
})
.where(eq(quizSessions.id, quizSession.id));
return NextResponse.json({
categoryCorrect,
specificityCorrect,
goldCategory: gold.goldCategory,
goldSpecificity: gold.goldSpecificity,
explanation: gold.explanation,
warmupCompleted: newCompleted,
done: newCompleted >= WARMUP_PARAGRAPHS.length,
});
}