diff --git a/backend/server.js b/backend/server.js index 0cceb72..e00f4d9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -886,6 +886,32 @@ app.post('/api/scenarios/:id/time', (req, res) => { res.json({ ok: true }); }); +// POST /api/progress/record — Phase 2A addition. Records a scenario outcome WITHOUT +// re-running validation. Exists because scenario validation itself must run against the +// student's own live cluster (inside their ephemeral Lab container), but the canonical +// progress record needs to live here, on the persistent identity instance — the Gateway +// calls this after a real validate/answer already succeeded against the student's Lab, to +// relay the outcome. See docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md "Progress relay." +app.post('/api/progress/record', (req, res) => { + const { scenario_id, status } = req.body || {}; + if (!scenario_id || !['completed', 'in_progress'].includes(status)) { + return res.status(400).json({ error: 'scenario_id and a valid status are required' }); + } + const studentId = getStudentId(req); + const progress = loadProgress(studentId); + const prev = progress[scenario_id] || { attempts: 0 }; + progress[scenario_id] = { + ...prev, + status, + attempts: (prev.attempts || 0) + 1, + last_validated: new Date().toISOString(), + completed_at: status === 'completed' ? new Date().toISOString() : prev.completed_at, + }; + saveProgress(studentId, progress); + touchStudent(studentId); + res.json({ ok: true }); +}); + // GET /api/progress — full progress summary app.get('/api/progress', (req, res) => { const scenarios = loadScenarios();