envia), resposta JSON: // {"ok":true,"ref":"T-1234","track_url":"https://…"} // {"ok":false,"error":"…"} // // Ao contrário dos outros endpoints desta pasta, este NÃO se autentica por // chave: quem chama é o browser de um administrador com sessão aberta neste // site. Precisa por isso da sessão e dos helpers de autenticação do site — e // obtém-nos do mesmo hub_bootstrap.php que os outros, que abre o config/db.php // do site e deixa em cima da mesa a sessão aberta, o $pdo e o functions_hub. // // Assim o stub de cada site é uma linha, igual à dos restantes endpoints. // // Sendo autenticado por cookie, é atacável por CSRF: um site qualquer podia // levar o browser de um administrador a abrir tickets em nome dele. Daí o // token do platform_csrf_ok(). Não substitui a verificação do papel — CSRF e // permissão respondem a perguntas diferentes. declare(strict_types=1); require_once __DIR__ . '/../hub_bootstrap.php'; require_once __DIR__ . '/../functions_feedback.php'; require_once __DIR__ . '/../functions_csrf.php'; header('Content-Type: application/json; charset=utf-8'); // Nada disto deve ficar em cache nem ser partilhado entre sessões. header('Cache-Control: no-store'); function feedback_out(int $status, array $payload): never { http_response_code($status); echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { feedback_out(405, ['ok' => false, 'error' => 'Usa POST.']); } $user = platform_feedback_user(); if (!$user || !platform_feedback_is_admin($user)) { // 403 e não 401: quem chega aqui sem sessão não tem como a obter a partir // deste endereço, e um 401 mandaria o browser pedir credenciais. feedback_out(403, ['ok' => false, 'error' => 'Sessão terminada ou sem permissão. Recarrega a página.']); } if (!platform_csrf_ok()) { feedback_out(403, ['ok' => false, 'error' => 'Pedido rejeitado (token expirado). Recarrega a página e tenta outra vez.']); } $res = platform_feedback_submit([ 'title' => (string) ($_POST['title'] ?? ''), 'description' => (string) ($_POST['description'] ?? ''), 'category' => (string) ($_POST['category'] ?? 'problema'), 'blocking' => !empty($_POST['blocking']), 'page_url' => (string) ($_POST['page_url'] ?? ''), ], $user); // 200 mesmo quando o hub recusa: o erro já vem escrito para a pessoa ler, e é // o corpo que a bolha mostra. Um 4xx aqui só faria o fetch tratar como avaria // de rede uma coisa que tem explicação. feedback_out(200, [ 'ok' => (bool) $res['ok'], 'error' => (string) ($res['error'] ?? ''), 'ref' => (string) ($res['ref'] ?? ''), 'track_url' => (string) ($res['track_url'] ?? ''), ]);