check(Http::clientIp())) { Http::fail(429, 'rate_limited', 'Zu viele Reports. Bitte Sendefrequenz reduzieren.'); } $data = Http::body(); if ($data === []) { Http::fail(400, 'empty_body', 'Der Request-Body ist leer.'); } $environment = is_string($data['environment'] ?? null) ? $data['environment'] : null; // Session ist hier bewusst nicht erlaubt: dieser Endpunkt ist die // Maschinenschnittstelle. Das WebUI legt Items ueber index.php an. $context = ApiAuth::requireScope($db, 'bugtracker:report', $environment, false); $projectSlug = is_string($data['project_slug'] ?? null) ? trim($data['project_slug']) : null; ApiAuth::enforceProject($context, $projectSlug); // Ein projektgebundenes Token schreibt immer in sein eigenes Projekt. $boundProject = ApiAuth::projectFilter($context); if ($boundProject !== null) { $data['project_slug'] = $boundProject; } // Der Absender wird aus dem Token abgeleitet und kann nicht frei gewaehlt // werden - sonst koennte sich ein Agent als ein anderer ausgeben. $data['created_by'] = $context['actor']; // Idempotenz-Schluessel darf auch als Header kommen. if (empty($data['client_ref'])) { $headerRef = Http::header('idempotency-key'); if ($headerRef !== null) { $data['client_ref'] = $headerRef; } } $repo = new BugRepo($db); $result = $repo->reportItem($data); $message = $result['idempotent_hit'] ? 'Bereits erfasst (identische client_ref) - kein Duplikat angelegt.' : ($result['is_new'] ? ($result['type'] === 'bug' ? 'Bug erfasst.' : 'Feature Request erfasst.') : 'Wiederkehrendes Vorkommnis - Zaehler erhoeht.'); Http::ok([ 'item_id' => $result['id'], 'is_new' => $result['is_new'], 'idempotent_hit' => $result['idempotent_hit'], 'occurrence_count' => $result['occurrence_count'], 'dedup_key' => $result['dedup_key'], 'error_hash' => $result['error_hash'], 'type' => $result['type'], 'item_status' => $result['status'], 'environment' => $result['environment'], 'push_id' => $result['push_id'] ?? null, 'regression_of' => $result['regression_of'] ?? null, 'url' => Http::baseUrl() . '/index.php#tab-bugtracker', 'message' => $message, ], $result['is_new'] ? 201 : 200);