A Node.js application that queries uNivUS directly for real-time NUS shuttle bus positions, speeds, passenger loads, and crowd levels, stores observations in shared Supabase PostgreSQL storage, and visualizes live vehicle tracking and historical commute analytics. The backend uses Node.js with native PostgreSQL (pg) connection pooling for Supabase; the frontend uses Leaflet for interactive campus mapping, high-precision road-snapped route tracing, and Chart.js-style canvas visualizations.
- Live Campus Shuttle Map: Real-time GPS locations and bearing markers for all active NUS shuttle buses across Kent Ridge campus.
- Road-Snapped Route Traces: Realistic, high-resolution route highlights for routes
A1,A2,D1,D2,K,R1, andR2that trace actual campus roads (snapped to road networks without cutting through buildings). Includes a toggle checkbox (Show Route Highlights) on the map. - Vehicle Telemetry & Analytics Dashboard: Detailed modal inspection for individual buses showing plate number, route badge, live coordinates, speed, load factor, passenger headcount, and 24-hour occupancy/pax trend graphs.
- Direct Official uNivUS Integration: Authenticates as guest via official uNivUS API endpoints; no student login, manual API key, or expiring user credentials needed.
- Durable Supabase Storage: Centralized PostgreSQL database storing historical observations, aggregate hourly occupancy, and fleet states across Vercel serverless deployments.
- Mobile Responsive Design: Clean, responsive layout optimized for desktop, tablet, and mobile browsers with fluid touch controls and collapsible filters.
- Dismissible Live Status Banner: Dismissible connection status alert with one-click close button.
- Automated Background Polling: Built for serverless deployment with a GitHub Actions workflow (
.github/workflows/poll.yml) that can be triggered on schedule (e.g., via cron-job.org or native cron) to poll uNivUS and write directly to Supabase DB.
Requires Node.js 24+ and a Supabase database.
npm.cmd ci
Copy-Item .env.example .env
# Set SUPABASE_DB_URL in .env
npm.cmd start- Create a project at Supabase and copy the Connection Pooler URI (Transaction mode, port
6543) intoSUPABASE_DB_URLin.env. - Run
npm start. The server starts athttp://127.0.0.1:3000. - Open
http://127.0.0.1:3000in your browser. Click Poll Now to perform an immediate live collection.
Bus telemetry is collected and stored in Supabase DB through automated or on-demand execution:
- GitHub Actions (Recommended for Production):
- Workflow file:
.github/workflows/poll.yml - Runs
npm run poll -- --once(node scripts/poll.js --once). - Triggerable on a schedule via cron or external webhooks (e.g.
cron-job.orgvia GitHub repository dispatch). - Direct connection: connects directly to uNivUS, normalizes telemetry, and writes records straight to Supabase DB.
- Workflow file:
- On-Demand Poller Command:
npm run poll -- --once: Executes a single poll cycle, logs records count, and exits.npm run poll: Runs a continuous 10-minute polling daemon in Node.js.
- Web Dashboard (Reader):
- The web app fetches
/api/liveand/api/historyevery 30 seconds to refresh the interface. - Contains an on-demand Poll Now button for administrative manual refreshes.
- The web app fetches
- HTTP Cron Endpoint:
GET /api/crontriggers an immediate collection cycle in hosted serverless environments.
The adapter emulates the Continue as Guest flow in the official uNivUS web app:
GET https://inetapps.nus.edu.sg/univus/web/api/login/loginPubliccreates an official guest session.- Session cookies and anti-forgery tokens are held securely in memory.
POST https://inetapps.nus.edu.sg/univus/web/api/esbqueries the modern ESB proxy endpoint (/univus/api/bus-proxy/active-bus) for each route code (A1,A2,D1,D2,K, etc.).- The response yields
data.activebuscontaining precise latitude/longitude, speed, capacity, ridership, and crowd levels. - Sessions renew automatically within a 24-hour cycle or when response cookies expire. If a session is rejected, the client re-authenticates and retries.
- Strict Validation: Timestamps must be valid and within two minutes of current time. Active bus counts in payload headers must match the reported vehicles.
- Fail-Safe History: If a collection fails or uNivUS is temporarily unreachable, existing historical observations are preserved.
- Community Fallback: If configured in
automode, an outage can fall back to the public arrivals feed (bus.hewliyang.com) for monitored stops (UTOWN,KR-MRT). - Telemetry Sanitization: Missing readings stay
null(never fabricated). Genuine zero values (0 km/h speed, 0 ridership) are preserved as0. - Deduplication: Multi-route overlapping vehicle plates are counted once per poll batch.
- Stale Expiry: Observations older than 15 minutes are moved to historical fleet status.
- Time Zone Consistency: All historical analytics and timestamps are formatted in Asia/Singapore time (UTC+8).
| Variable | Description | Default |
|---|---|---|
SUPABASE_DB_URL |
Required. Supabase Transaction Pooler URI (postgresql://postgres.[project-ref]:[pwd]@aws-0-[region].pooler.supabase.com:6543/postgres?sslmode=require). |
— |
BUS_PROVIDER |
Data source mode: auto (default, direct uNivUS with community fallback), univus (direct only), community, or connectx. |
auto |
FMS_ROUTES |
Comma-separated route codes to collect. | A1,A2,D1,D2,E,K,R1,R2 |
BUS_STOPS |
Monitored stop codes for community fallback mode. | UTOWN,KR-MRT |
ADMIN_TOKEN |
Optional bearer secret for administrative settings modification and data deletion. | — |
HOST |
Bind address for local development server. | 127.0.0.1 |
PORT |
Port for local development server. | 3000 |
All tests run in completely isolated environments using in-memory databases and offline HTTP mocks. Tests never touch your live Supabase database or external providers.
# Run the complete test suite (193 tests)
npm test
# Run frontend UI, Leaflet map, and contract tests (26 tests)
node tests/test_frontend.js
# Run codebase syntax and asset integrity check
npm run check
# Verify direct uNivUS live feed connectivity without writing to DB
npm run verify-live -- --directNUS-Bus-Tracker/
├── .github/
│ └── workflows/
│ ├── cleanup.yml # Scheduled monthly retention cleanup
│ └── poll.yml # Scheduled GitHub Actions poller (npm run poll -- --once)
├── api/
│ └── index.js # Vercel serverless request handler
├── public/
│ ├── app.js # Frontend logic: Leaflet map, bus markers, road polylines, charts
│ ├── favicon.svg # Application bus icon
│ ├── index.html # Responsive dashboard UI layout & navigation
│ └── style.css # Dark theme, mobile responsive styles, modals
├── scripts/
│ ├── check.js # Linter and asset integrity validator
│ ├── cleanup.js # Database retention purge script
│ ├── poll.js # Standalone poller (supports --once for CI/cron)
│ ├── server.js # Local development HTTP server runner
│ └── verify_live.js # Live uNivUS telemetry diagnostic test tool
├── src/
│ ├── api_client.js # Telemetry normalizer & ConnectX fallback
│ ├── collector.js # Polling orchestrator & batch recorder
│ ├── database_errors.js # Safe Supabase error mapping
│ ├── db.js # Database client factory
│ ├── db_shared.js # Shared PostgreSQL schema DDL & analytics queries
│ ├── provider_config.js # Provider configuration validation
│ ├── provider_http.js # Bounded HTTP fetcher with timeout/size limits
│ ├── public_feed.js # Community arrival fallback provider
│ ├── remote_db.js # Supabase PostgreSQL schema, queries, and analytics
│ ├── routes.js # Canonical Kent Ridge route identifiers
│ ├── server.js # Universal HTTP request routing & API endpoints
│ ├── univus_auth.js # Legacy ConnectX guest token provider
│ └── univus_client.js # Official uNivUS ESB API client (ActiveBus)
├── tests/ # 193 unit & integration tests
│ ├── database_fixture.js # In-memory pg-mem fixture for isolated testing
│ ├── no_provider_network.js # Network isolation guard for tests
│ ├── run_all_tests.js # Main test runner invoked by npm test
│ ├── test_database.js # Unit tests for PostgreSQL query logic
│ ├── test_frontend.js # DOM, Leaflet map, chart, and HTML contract tests
│ ├── test_public_feed.js # Community arrival parsing tests
│ ├── test_remote_database.js # Supabase transactions, schema migrations & concurrency
│ ├── test_server_endpoints.js # HTTP API endpoint tests (/api/live, /api/history)
│ ├── test_suite.js # Data normalization & collector recovery tests
│ ├── test_univus_auth.js # ConnectX token provider tests
│ ├── test_univus_client.js # uNivUS ESB API parsing & session tests
│ └── test_vercel_handler.js # Serverless deployment edge case tests
├── package.json # Dependencies, test scripts, and Node 24 requirement
├── VERCEL.md # Deployment guide for Vercel + Supabase
└── vercel.json # Vercel deployment configuration
See VERCEL.md for full instructions on deploying the dashboard to Vercel and setting up the Supabase database and GitHub Actions cron poller.