- Add get-geo-stats-by-city function for city-level queries
- Add /api/asteroid/stats/geo/cities endpoint
- Add expandable country rows in admin geo stats table
- Click country to expand/collapse city breakdown
- Update update-geo-stats to accept optional city parameter
- Update get-cached-geo to cache and return city along with country
- Update collect-geo-stats-for-mount and collect-geo-stats-from-web-listeners
to track by country+city
- Revert migration to keep UNIQUE(date, country_code, city) constraint
Instead of relying on Icecast's listener IPs (which show proxy IPs
behind HAProxy), capture real client IPs from X-Forwarded-For header
when users visit the front page or audio player frame.
Radiance automatically extracts X-Forwarded-For into (remote *request*).
Changes:
- Add *web-listeners* hash table to track visitors with real IPs
- Add register-web-listener to capture IPs during page requests
- Add collect-geo-stats-from-web-listeners for polling
- Call register-web-listener from front-page and audio-player-frame
- Filter out private/internal IPs (172.x, 192.168.x, 10.x, 127.x)
- Remove session requirement - use IP hash as key for anonymous visitors
Drakma returns a byte vector, not a string. The code was passing
this directly to cl-json:decode-json-from-string which expects
a string, causing the lookup to silently fail.
- Add geo IP lookup for listener locations (ip-api.com)
- Add geo stats table with country flags to admin dashboard
- Fix listener stats table alignment with proper centering
- Fix Now Playing display to update without requiring audio playback
- Add caching for geo lookups to reduce API calls
- Add database schema for listener snapshots, sessions, and aggregates
- Implement background polling of Icecast admin XML stats
- Add API endpoints for current, daily, and geo stats
- Add listener stats section to admin dashboard with auto-refresh
- GDPR compliant: IP hashing, data retention cleanup