the best ios gas tracking app
1import { serve } from "bun";
2import { getDb, getStaleCells, markCellFetched, upsertStations } from "./db.ts";
3import { handleEIAAverages, refreshEIAAverages } from "./handlers/eia.ts";
4import { handleHealth } from "./handlers/health.ts";
5import { handleRegisterKey } from "./handlers/keys.ts";
6import { logged } from "./logger.ts";
7import { handleBbox, handleNearby, NEARBY_TTL_MS } from "./handlers/stations.ts";
8import { handlePrefetchRoute } from "./handlers/prefetch.ts";
9import { fetchStationsByLocation } from "./gasbuddy.ts";
10import { cellCenter } from "./geo.ts";
11
12// Initialize DB on startup
13getDb();
14
15// Kick off EIA refresh in background (won't block startup)
16refreshEIAAverages();
17
18// Proactively re-fetch stale cells that were requested in the last 24 hours.
19// Runs every 5 minutes; serializes GasBuddy calls with a 500ms delay.
20async function proactiveRefresh() {
21 const stale = getStaleCells(NEARBY_TTL_MS, 24 * 60 * 60 * 1000);
22 if (stale.length === 0) return;
23 console.log(`[refresh] ${stale.length} stale cells to refresh`);
24 for (const cellKey of stale) {
25 const { lat, lng } = cellCenter(cellKey);
26 try {
27 const stations = await fetchStationsByLocation(lat, lng);
28 upsertStations(stations);
29 } catch (e) {
30 console.error(`[refresh] GasBuddy fetch failed for ${cellKey}:`, e);
31 }
32 markCellFetched(cellKey); // always update timestamp to enforce backoff
33 await new Promise((r) => setTimeout(r, 500));
34 }
35}
36
37setInterval(proactiveRefresh, 30 * 60 * 1000); // every 30 minutes
38
39const server = serve({
40 port: process.env.PORT ? parseInt(process.env.PORT, 10) : 7878,
41
42 routes: {
43 "/health": { GET: logged(handleHealth) },
44 "/keys/register": { POST: logged(handleRegisterKey) },
45 "/stations/nearby": { GET: logged(handleNearby) },
46 "/stations/bbox": { GET: logged(handleBbox) },
47 "/prefetch/route": { POST: logged(handlePrefetchRoute) },
48 "/eia/averages": { GET: logged(handleEIAAverages) },
49 },
50
51 fetch(req) {
52 const url = new URL(req.url);
53 console.log(`${req.method} ${url.pathname} → 404`);
54 return new Response("Not found", { status: 404 });
55 },
56});
57
58console.log(`overpass listening on ${server.hostname}:${server.port}`);
59
60process.on("SIGINT", () => process.exit(0));
61process.on("SIGTERM", () => process.exit(0));
62
63process.on("unhandledRejection", (reason) => {
64 console.error("Unhandled rejection:", reason);
65});