seedproject-web/api/app/Controllers/JsonController.php

49 lines
1.8 KiB
PHP
Raw Permalink Normal View History

<?php
namespace App\Controllers;
/** Base for JSON endpoints: response envelope + IP throttle. Extends core \Controller. */
class JsonController extends \Controller
{
/** Emit { ok, data, error } with the right HTTP status, then stop. */
protected function json($data = null, int $status = 200, ?array $error = null): void
{
http_response_code($status);
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
'ok' => $error === null,
'data' => $data,
'error' => $error, // ['code' => ..., 'message' => ...] or null
]);
exit;
}
/**
* Returns true when the caller has EXCEEDED $max hits on $key within $window seconds.
* Reuses the api_requests table (no new table needed).
*/
protected function throttle(string $key, int $max, int $window): bool
{
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
// Trusted server-local / whitelisted callers are never throttled: the static-site build
// fetches the read API from the box itself thousands of times per build. The throttle is
// for public abuse (real client IPs), not our own build. See Functions::isTrustedIp.
if (\Functions::isTrustedIp($ip)) {
return false;
}
$count = (int) \Db::getValue(
"SELECT COUNT(*) FROM `api_requests`
WHERE `requesting_ip` = ? AND `request` = ?
AND `created_date` > (NOW() - INTERVAL ? SECOND)",
[$ip, $key, $window]
);
\Db::insert('api_requests', [
'requesting_ip' => $ip,
'request' => $key,
'service' => 'foundation',
'domainURI' => $_SERVER['HTTP_HOST'] ?? '',
'created_date' => date('Y-m-d H:i:s'),
]);
return $count >= $max;
}
}