// ═══════════════════════════════════════════════════════════════════════════ // DOUBT API - Submit and resolve student doubts // ═══════════════════════════════════════════════════════════════════════════ // POST /api/doubt - Create doubt or solve doubt // GET /api/doubt?sessionId=xxx - Get all doubts for a session // ═══════════════════════════════════════════════════════════════════════════ import { NextRequest, NextResponse } from 'next/server'; import { sessionManager } from '@/lib/session-manager'; export { OPTIONS } from '@/lib/cors'; export const runtime = 'nodejs'; export const dynamic = 'force-dynamic'; // ═══════════════════════════════════════════════════════════════════════════ // GET DOUBTS // ═══════════════════════════════════════════════════════════════════════════ // GET /api/doubt?sessionId=xxx // Returns all doubts for a session // ═══════════════════════════════════════════════════════════════════════════ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const sessionId = searchParams.get('sessionId'); if (!sessionId) { return NextResponse.json({ success: false, error: 'Missing required parameter: sessionId' }, { status: 400 }); } const session = sessionManager.getSession(sessionId); if (!session) { return NextResponse.json({ success: false, error: 'Session not found' }, { status: 404 }); } const doubts = sessionManager.getDoubts(sessionId); // Group by status const pending = doubts.filter(d => d.status === 'pending'); const inProgress = doubts.filter(d => d.status === 'in_progress'); const solved = doubts.filter(d => d.status === 'solved'); return NextResponse.json({ success: true, data: { doubts, summary: { total: doubts.length, pending: pending.length, inProgress: inProgress.length, solved: solved.length } } }); } // ═══════════════════════════════════════════════════════════════════════════ // DOUBT ACTIONS // ═══════════════════════════════════════════════════════════════════════════ export async function POST(request: NextRequest) { try { const body = await request.json(); const { action, sessionId } = body; // Validate sessionId if (!sessionId) { return NextResponse.json({ success: false, error: 'Missing required field: sessionId' }, { status: 400 }); } // Check session exists const session = sessionManager.getSession(sessionId); if (!session) { return NextResponse.json({ success: false, error: 'Session not found' }, { status: 404 }); } switch (action) { case 'create': return handleCreate(sessionId, body); case 'solve': return handleSolve(sessionId, body); case 'update': return handleUpdate(sessionId, body); default: return NextResponse.json({ success: false, error: `Invalid action: ${action}. Valid actions: create, solve, update` }, { status: 400 }); } } catch (error) { return NextResponse.json({ success: false, error: 'Invalid JSON body' }, { status: 400 }); } } // ═══════════════════════════════════════════════════════════════════════════ // CREATE DOUBT // ═══════════════════════════════════════════════════════════════════════════ // POST /api/doubt // Body: { // action: 'create', // sessionId: string, // Required: Session ID // studentId: string, // Required: Student's unique ID // studentName: string, // Required: Student's display name // question: string, // Required: The doubt/question // image?: string, // Optional: Base64 encoded image // slideNumber?: number // Optional: Slide number where doubt occurred // } // ═══════════════════════════════════════════════════════════════════════════ async function handleCreate(sessionId: string, body: any) { const { studentId, studentName, question, image, slideNumber } = body; // Validate required fields if (!studentId) { return NextResponse.json({ success: false, error: 'Missing required field: studentId' }, { status: 400 }); } if (!studentName) { return NextResponse.json({ success: false, error: 'Missing required field: studentName' }, { status: 400 }); } if (!question || typeof question !== 'string' || question.trim().length === 0) { return NextResponse.json({ success: false, error: 'Missing required field: question' }, { status: 400 }); } // Validate image if provided if (image && !image.startsWith('data:image/')) { return NextResponse.json({ success: false, error: 'Invalid image format. Must be base64 data URL starting with "data:image/"' }, { status: 400 }); } // Create doubt const doubt = await sessionManager.createDoubt( sessionId, studentId, studentName, question.trim(), image, slideNumber ); return NextResponse.json({ success: true, data: doubt, message: 'Doubt submitted successfully' }); } // ═══════════════════════════════════════════════════════════════════════════ // SOLVE DOUBT // ═══════════════════════════════════════════════════════════════════════════ // POST /api/doubt // Body: { // action: 'solve', // sessionId: string, // Required: Session ID // doubtId: string, // Required: Doubt ID to solve // annotatedImage?: string, // Optional: Base64 annotated image // teacherResponse?: string // Optional: Text response from teacher // } // ═══════════════════════════════════════════════════════════════════════════ async function handleSolve(sessionId: string, body: any) { const { doubtId, annotatedImage, teacherResponse } = body; // Validate required fields if (!doubtId) { return NextResponse.json({ success: false, error: 'Missing required field: doubtId' }, { status: 400 }); } // Validate image if provided if (annotatedImage && !annotatedImage.startsWith('data:image/')) { return NextResponse.json({ success: false, error: 'Invalid annotatedImage format. Must be base64 data URL' }, { status: 400 }); } // Solve doubt const doubt = await sessionManager.solveDoubt( sessionId, doubtId, annotatedImage, teacherResponse ); if (!doubt) { return NextResponse.json({ success: false, error: 'Doubt not found' }, { status: 404 }); } return NextResponse.json({ success: true, data: doubt, message: 'Doubt resolved successfully' }); } // ═══════════════════════════════════════════════════════════════════════════ // UPDATE DOUBT STATUS // ═══════════════════════════════════════════════════════════════════════════ // POST /api/doubt // Body: { // action: 'update', // sessionId: string, // Required: Session ID // doubtId: string, // Required: Doubt ID // status: 'in_progress' // Required: New status // } // ═══════════════════════════════════════════════════════════════════════════ async function handleUpdate(sessionId: string, body: any) { const { doubtId, status } = body; if (!doubtId) { return NextResponse.json({ success: false, error: 'Missing required field: doubtId' }, { status: 400 }); } if (!status || !['pending', 'in_progress', 'solved'].includes(status)) { return NextResponse.json({ success: false, error: 'Invalid status. Must be: pending, in_progress, or solved' }, { status: 400 }); } // Get doubts const doubts = sessionManager.getDoubts(sessionId); const doubt = doubts.find(d => d.id === doubtId); if (!doubt) { return NextResponse.json({ success: false, error: 'Doubt not found' }, { status: 404 }); } // Update status doubt.status = status as any; // Broadcast update await sessionManager.broadcast(sessionId, 'doubt_updated', doubt); return NextResponse.json({ success: true, data: doubt, message: 'Doubt status updated' }); }