import { requireAuth } from "@/lib/jwt"; import { sql } from "@/lib/db"; import { boundingBox, haversine } from "@/lib/utils"; import { DRIVER_STALE_SECONDS } from "@/constants/dispatch"; // GET — online drivers of `service` near (lat,lng), for the rider map and the // "drivers near you" count on the request screen. Only vetted, logged-in // drivers (approved + user_id IS NOT NULL) with a fresh location ping are // returned; legacy seed rows have no position and are never shown to riders. // // Query: ?service=car&lat=33.89&lng=35.50&radius=8000 // // The radius is enforced, not decorative. Returning every online driver in the // country to any signed-in account turns this endpoint into a live tracker for // the whole fleet; bounding it means a caller only ever learns about cars they // could plausibly hail. A coarse bounding box does the work in the index, then // a great-circle pass trims the corners. const DEFAULT_RADIUS_M = 8000; const MAX_RADIUS_M = 20000; // Drivers are returned at ~11m precision (4 decimal places). That is well // inside "which street is the car on" for a map pin, and stops the endpoint // from being a metre-accurate trace of someone's working day. const COORD_PRECISION = 1e4; const snap = (value: number): number => Math.round(value * COORD_PRECISION) / COORD_PRECISION; export async function GET(req: Request) { const auth = requireAuth(req); if ("error" in auth) return auth.error; try { const url = new URL(req.url); const service = url.searchParams.get("service") ?? "car"; const lat = Number(url.searchParams.get("lat")); const lng = Number(url.searchParams.get("lng")); if (Number.isNaN(lat) || Number.isNaN(lng)) { return Response.json( { error: "lat and lng query params are required numbers." }, { status: 400 }, ); } const requested = Number(url.searchParams.get("radius")); const radius = Number.isFinite(requested) && requested > 0 ? Math.min(requested, MAX_RADIUS_M) : DEFAULT_RADIUS_M; const box = boundingBox(lat, lng, radius); const rows = await sql<{ id: number; latitude: number; longitude: number; heading: number | null; speed_kph: number | null; }>` SELECT id, first_name, last_name, profile_image_url, car_image_url, car_seats, rating, service, car_model, latitude, longitude, heading, speed_kph, last_seen FROM drivers WHERE service = ${service} AND online = TRUE AND approval_status = 'approved' AND user_id IS NOT NULL AND last_seen > CURRENT_TIMESTAMP - make_interval(secs => ${DRIVER_STALE_SECONDS}) AND latitude IS NOT NULL AND longitude IS NOT NULL AND latitude BETWEEN ${box.minLat} AND ${box.maxLat} AND longitude BETWEEN ${box.minLng} AND ${box.maxLng} `; const nearby = rows .filter((d) => haversine(lat, lng, d.latitude, d.longitude) <= radius) .map((d) => ({ ...d, latitude: snap(d.latitude), longitude: snap(d.longitude), })); return Response.json({ data: nearby }); } catch (error) { console.error("[DRIVER_NEARBY]: ", error); return Response.json({ error: "Internal Server Error" }, { status: 500 }); } }