Files

37 lines
1.5 KiB
PHP
Raw Permalink Normal View History

<?php
/**
* GET /api/locate.php — approximate, non-invasive location for the campus recommendation.
* Returns city-level coordinates only. Nothing is stored beyond a short hashed cache.
*/
declare(strict_types=1);
require_once __DIR__ . '/lib/bootstrap.php';
require_once __DIR__ . '/lib/Validator.php';
require_once __DIR__ . '/lib/RateLimit.php';
require_once __DIR__ . '/lib/GeoIp.php';
Api::requireMethod('GET');
$ip = Api::clientIp();
$rl = RateLimit::hit(RateLimit::ipBucket('locate', $ip), 30, 600);
if (!$rl['allowed']) {
Api::json(['ok' => false, 'reason' => 'rate_limited'], 429, ['Retry-After' => (string)$rl['retryAfter']]);
}
// Local development helper: ?debug_ip=8.8.8.8 (only when DEBUG_GEO=true)
if (Config::bool('DEBUG_GEO', false) && !empty($_GET['debug_ip']) && filter_var($_GET['debug_ip'], FILTER_VALIDATE_IP)) {
$ip = (string)$_GET['debug_ip'];
}
if (Config::bool('DEBUG_GEO', false) && isset($_GET['debug_lat'], $_GET['debug_lng'])) {
Api::json(['ok' => true, 'approximate' => ['lat' => (float)$_GET['debug_lat'], 'lng' => (float)$_GET['debug_lng'], 'city' => 'Debug', 'confidence' => 'low']]);
}
$loc = GeoIp::locate($ip);
if (!$loc) {
Api::json(['ok' => false, 'reason' => 'unavailable'], 200, ['Cache-Control' => 'private, max-age=300']);
}
Api::json([
'ok' => true,
'approximate' => ['lat' => $loc['lat'], 'lng' => $loc['lng'], 'city' => $loc['city'], 'region' => $loc['region'], 'confidence' => 'low'],
], 200, ['Cache-Control' => 'private, max-age=3600']);