Next.js 16 static export + PHP 8 API for cPanel/Apache hosting. - Location-aware campus finder (IP estimate, optional precise location, ZIP search) - Three-step Plan Your Visit form with separate email/SMS consent - PHP API: validation, anti-spam, rate limiting, SQLite lead store, Mailchimp and Sinch Engage adapters, idempotent retries via cron - Attribution capture (utm_*, gclid/gbraid/wbraid, fbclid, ttclid, twclid) and consent-aware analytics hooks - Vitest and PHP test suites, cPanel deployment script and docs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
29 lines
1.0 KiB
PHP
29 lines
1.0 KiB
PHP
<?php
|
|
/** GET /api/geocode.php?zip=02763 — US ZIP centroid lookup for the manual campus search. */
|
|
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/Geocode.php';
|
|
|
|
Api::requireMethod('GET');
|
|
|
|
$ip = Api::clientIp();
|
|
$rl = RateLimit::hit(RateLimit::ipBucket('geocode', $ip), 40, 600);
|
|
if (!$rl['allowed']) {
|
|
Api::json(['ok' => false, 'error' => 'Too many searches. Please wait a moment.'], 429, ['Retry-After' => (string)$rl['retryAfter']]);
|
|
}
|
|
|
|
$zip = isset($_GET['zip']) ? preg_replace('/\D/', '', (string)$_GET['zip']) : '';
|
|
$zip = substr((string)$zip, 0, 5);
|
|
if (strlen($zip) !== 5) {
|
|
Api::json(['ok' => false, 'error' => 'Enter a 5-digit ZIP code.'], 400);
|
|
}
|
|
|
|
$loc = Geocode::zip($zip);
|
|
if (!$loc) {
|
|
Api::json(['ok' => false, 'error' => "We couldn't find that ZIP. Try another or pick a campus below."], 200);
|
|
}
|
|
Api::json(['ok' => true, 'location' => $loc], 200, ['Cache-Control' => 'public, max-age=86400']);
|