export default { async fetch(request, env, ctx) { const url = new URL(request.url); const path = url.pathname; const method = request.method; // API ENDPOINTS if (path.startsWith('/api/')) { // CORS Headers const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', 'Content-Type': 'application/json' }; if (method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } // GET /api/students -> Ambil semua siswa (Realtime Sync & Teacher Dashboard) if (path === '/api/students' && method === 'GET') { try { const { results } = await env.DB.prepare('SELECT * FROM students ORDER BY name ASC').all(); const parsed = results.map(s => ({ ...s, introCompleted: Boolean(s.intro_completed), empathizeCompleted: Boolean(s.empathize_completed), defineCompleted: Boolean(s.define_completed), ideateCompleted: Boolean(s.ideate_completed), prototypeCompleted: Boolean(s.prototype_completed), testCompleted: Boolean(s.test_completed), challengeCompleted: Boolean(s.challenge_completed), evalCompleted: Boolean(s.eval_completed), evalScore: s.eval_score, reflectionCompleted: Boolean(s.reflection_completed), studentClass: s.student_class, lastActive: s.last_active, prototypeData: JSON.parse(s.prototype_data || '{}'), iterationStatement: s.iteration_statement || '', reflections: JSON.parse(s.reflections || '{}') })); return new Response(JSON.stringify(parsed), { headers: corsHeaders }); } catch (err) { return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: corsHeaders }); } } // POST /api/students/login -> Login atau Registrasi Siswa Baru if (path === '/api/students/login' && method === 'POST') { try { const body = await request.json(); const { name, studentClass } = body; const id = 'std_' + name.toLowerCase().replace(/[^a-z0-9]/g, '_') + '_' + studentClass.toLowerCase().replace(/[^a-z0-9]/g, '_'); const lastActive = new Date().toLocaleString('id-ID'); let { results } = await env.DB.prepare('SELECT * FROM students WHERE id = ?').bind(id).all(); let student; if (results.length === 0) { await env.DB.prepare(` INSERT INTO students (id, name, student_class, progress, intro_completed, empathize_completed, define_completed, ideate_completed, prototype_completed, test_completed, challenge_completed, eval_completed, eval_score, reflection_completed, last_active, prototype_data, iteration_statement, reflections) VALUES (?, ?, ?, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0, ?, '{}', '', '{}') `).bind(id, name, studentClass, lastActive).run(); const fetched = await env.DB.prepare('SELECT * FROM students WHERE id = ?').bind(id).first(); student = fetched; } else { await env.DB.prepare('UPDATE students SET last_active = ? WHERE id = ?').bind(lastActive, id).run(); student = results[0]; } const parsed = { ...student, introCompleted: Boolean(student.intro_completed), empathizeCompleted: Boolean(student.empathize_completed), defineCompleted: Boolean(student.define_completed), ideateCompleted: Boolean(student.ideate_completed), prototypeCompleted: Boolean(student.prototype_completed), testCompleted: Boolean(student.test_completed), challengeCompleted: Boolean(student.challenge_completed), evalCompleted: Boolean(student.eval_completed), evalScore: student.eval_score, reflectionCompleted: Boolean(student.reflection_completed), studentClass: student.student_class, lastActive: student.last_active, prototypeData: JSON.parse(student.prototype_data || '{}'), iterationStatement: student.iteration_statement || '', reflections: JSON.parse(student.reflections || '{}') }; return new Response(JSON.stringify(parsed), { headers: corsHeaders }); } catch (err) { return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: corsHeaders }); } } // POST /api/students/update -> Update Progress Siswa ke D1 Database if (path === '/api/students/update' && method === 'POST') { try { const body = await request.json(); const { id, progress, introCompleted, empathizeCompleted, defineCompleted, ideateCompleted, prototypeCompleted, testCompleted, challengeCompleted, evalCompleted, evalScore, reflectionCompleted, prototypeData, iterationStatement, reflections } = body; const lastActive = new Date().toLocaleString('id-ID'); await env.DB.prepare(` UPDATE students SET progress = ?, intro_completed = ?, empathize_completed = ?, define_completed = ?, ideate_completed = ?, prototype_completed = ?, test_completed = ?, challenge_completed = ?, eval_completed = ?, eval_score = ?, reflection_completed = ?, last_active = ?, prototype_data = ?, iteration_statement = ?, reflections = ? WHERE id = ? `).bind( progress || 0, introCompleted ? 1 : 0, empathizeCompleted ? 1 : 0, defineCompleted ? 1 : 0, ideateCompleted ? 1 : 0, prototypeCompleted ? 1 : 0, testCompleted ? 1 : 0, challengeCompleted ? 1 : 0, evalCompleted ? 1 : 0, evalScore !== undefined ? evalScore : null, reflectionCompleted ? 1 : 0, lastActive, JSON.stringify(prototypeData || {}), iterationStatement || '', JSON.stringify(reflections || {}), id ).run(); return new Response(JSON.stringify({ success: true, lastActive }), { headers: corsHeaders }); } catch (err) { return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: corsHeaders }); } } } // TAMPILKAN FRONTEND HTML LENGKAP return new Response(HTML_CONTENT, { headers: { 'Content-Type': 'text/html; charset=utf-8' } }); } }; const HTML_CONTENT = ` Design Thinking & Prototype - Media Pembelajaran KIK Kelas XI SMK
INNOVATION LAB KIK FASE F / XI SMK
`;