- Fix listener_count accumulation bug: use GREATEST() instead of + to track
peak concurrent listeners per day rather than cumulative count
- Migrate all inline JavaScript from templates to ParenScript:
- admin.ctml: listener stats, geo stats, password reset -> admin.lisp
- audio-player-frame.ctml: stream player logic -> stream-player.lisp (new)
- popout-player.ctml: popout player logic -> stream-player.lisp (shared)
- frameset-wrapper.ctml: frame-busting -> frameset-utils.lisp (new)
- profile.ctml: removed redundant inline init (already in profile.lisp)
- Fix API route detection: handle URIs with or without leading slash
- Add routes to serve new ParenScript-generated JS files
- Update ASDF system definition with new ParenScript components
Tested locally for 8+ hours with no issues.
- Add user password change functionality in profile page
- Add admin password reset functionality in admin dashboard
- Fix listener count to show total from Icecast root tag
- Replace concatenate with format
- Add with-error-handling to API endpoints
- Make Icecast port binding configurable via ICECAST_BIND env var
- Add comprehensive docstrings to public functions
Note: Password reset has known issue with LambdaLite db:update
not updating password-hash field. Issue reported as an issue.