2984d93e7e
Sicherheit: - progress.php: IDOR geschlossen (student_id-Zweig verlangt Lehrer-Login + Klassenbesitz) - licenses.php: ?admin=1 und generate nur noch fuer echten Admin; Collision-Retry mit Cap - .htaccess: .env/.sh/.sql/.bak Deny (Defense-in-Depth) Schueler-Ergebnisse (kritisch): - sim-metrics.js + .ivm-CSS in schueler.html; renderOwnRunCard-Fallback zeigt jetzt Sterne + grafische Kennzahlen fuer die 9 bisher leeren Sims - sim-metrics.js: klima final_budget als "Mio EUR" statt "EUR" (Faktor 10^6) Skalierung: - assessment.php: Zwischen-Pings (phase running/started) nicht mehr persistiert - heli: 30-s-assessmentPing entfernt (Live-Zustand laeuft ueber Heartbeat) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
55 lines
2.0 KiB
PHP
55 lines
2.0 KiB
PHP
<?php
|
|
/**
|
|
* API: Assessment-Daten speichern
|
|
* POST /api/assessment {simId, processLog, predictions, results, reflections, duration, completedPhases}
|
|
*/
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
Response::error('Nur POST erlaubt', 405);
|
|
}
|
|
|
|
$sessionId = Session::requireStudent();
|
|
$body = json_decode(file_get_contents('php://input'), true);
|
|
if (!$body || !isset($body['simId'])) {
|
|
Response::error('simId erforderlich');
|
|
}
|
|
|
|
$db = Database::get();
|
|
|
|
// class_id aus student_session holen
|
|
$session = $db->fetchOne('SELECT class_id FROM student_sessions WHERE id = ?', [$sessionId]);
|
|
$classId = $session ? $session['class_id'] : null;
|
|
|
|
// Cap defensiv: nie >120 min in die DB (siehe GGS_MAX_SESSION_MS).
|
|
if (!defined('GGS_MAX_SESSION_MS')) define('GGS_MAX_SESSION_MS', 7200000);
|
|
$rawDuration = (int)($body['duration'] ?? 0);
|
|
$durationMs = max(0, min($rawDuration, GGS_MAX_SESSION_MS));
|
|
|
|
// Zwischen-Telemetrie eines laufenden Durchgangs NICHT in assessments persistieren.
|
|
// Der Live-Zustand für das Lehrer-Cockpit kommt über den Heartbeat (live_sessions);
|
|
// diese 30-s-Pings (phase 'running'/'started') haben früher die Tabelle zugemüllt
|
|
// (~96 % der Zeilen). Nur echte Abschlüsse/Ergebnisse werden gespeichert.
|
|
$pl = $body['processLog'] ?? [];
|
|
$phase = (is_array($pl) && isset($pl['phase'])) ? $pl['phase'] : null;
|
|
if ($phase === 'running' || $phase === 'started') {
|
|
Response::ok(['skipped' => true]);
|
|
}
|
|
|
|
$db->execute(
|
|
'INSERT INTO assessments (session_id, sim_id, class_id, process_log, predictions, results, reflections, duration_ms, completed_phases)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
[
|
|
$sessionId,
|
|
$body['simId'],
|
|
$classId,
|
|
json_encode($body['processLog'] ?? []),
|
|
json_encode($body['predictions'] ?? []),
|
|
json_encode($body['results'] ?? []),
|
|
json_encode($body['reflections'] ?? []),
|
|
$durationMs,
|
|
json_encode($body['completedPhases'] ?? []),
|
|
]
|
|
);
|
|
|
|
Response::ok(['id' => $db->lastInsertId()]);
|