shared/platform-tools was a git submodule. On this branch it is a plain
directory of tracked files, frozen at ea3616abae30106df283e73a3e2f9e5bb0e6ae90
— the same commit fotografia.andmore.pt pins. Contents are byte-identical to
that tree, verified blob by blob.
The submodule caused every deployment failure this site has had:
- .gitmodules named no `branch`, so the update fell back to
refs/remotes/origin/HEAD — a ref a plain server-side clone never writes.
"Atualizar submódulo" failed with "Unable to find refs/remotes/origin/HEAD
revision in submodule path".
- Its URL was http://, which the origin answers with 523.
- This install is deployed by uploading files, and a submodule is invisible
to that. The tree moved forward while shared/platform-tools stayed on the
old pin, config/functions_secrets.php died on a missing compat_secrets.php,
and the whole site — public pages included — went down.
Each of those was separately fixable. Together they mean a second repository
nobody can see from the deployment path, gating whether the first one boots. A
plain directory deploys like every other file and cannot fall out of step.
The cost is that platform-tools updates no longer arrive by bumping a pin; they
are a copy-over-and-commit. shared/platform-tools/VENDORED.md records the
upstream SHA, the procedure, and which four files differ from upstream in line
endings only (social/*.js, .css, functions_publications.php — CRLF upstream,
normalised to LF here; content otherwise identical).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
3.0 KiB
PHP
71 lines
3.0 KiB
PHP
<?php
|
|
// ─── Endpoint da bolha de feedback ───────────────────────────────────────────
|
|
//
|
|
// POST multipart/form-data (o que um <form> 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 é quem sabe distinguir
|
|
// os dois stacks da frota (config/db.php ou src/config.php + App\Database) 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'] ?? ''),
|
|
]);
|