| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // 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' | |
| }); | |
| } | |