Added back password management APIs using handler-case (not with-error-handling):
1. /api/asteroid/user/change-password
- Users can change their own password
- Requires current password verification
- Uses get-current-user() function
2. /api/asteroid/admin/reset-password
- Admins can reset any user's password
- No current password required
- Returns 404 if user not found
Both APIs use handler-case for error handling to maintain consistency
with other APIs in this file. Build succeeds.
Works with password change UI in admin dashboard (admin.ctml, admin.js).
As easilok correctly pointed out in IRC:
- Inside container: Liquidsoap must bind to 0.0.0.0 (all interfaces)
- Security is enforced by Docker port mapping: 127.0.0.1🔢1234
- This prevents external access while allowing container functionality
The docker-compose.yml mapping is what provides security, not the
bind address inside the container.
Credit: easilok for the explanation and fix approach
Reverted to working version before password API additions.
The password APIs and UI are still in place (admin.ctml, admin.js).
Need to re-add the backend APIs more carefully.
Build now succeeds.
Fixed compilation error by using the correct function name.
The codebase uses get-current-user() which is defined in
user-management.lisp, not auth:current-user.
Build now completes successfully.
Changed password change and reset APIs to use with-error-handling
macro instead of handler-case for consistency with refactored codebase.
This ensures proper error handling using our custom condition system.
NEW FEATURE: Password Change Form on Admin Dashboard
TEMPLATE CHANGES (admin.ctml):
- Added 'Account Security' section after System Status
- Password change form with:
- Current password field
- New password field (min 8 characters)
- Confirm password field
- Submit button
- Message display area for feedback
JAVASCRIPT CHANGES (admin.js):
- changeAdminPassword(event) function
- Validates passwords match
- Validates minimum length (8 chars)
- Calls /api/asteroid/user/change-password
- Shows success/error messages
- Clears form on success
- showPasswordMessage(message, type) helper
- Displays success/error messages
- Auto-hides success messages after 5 seconds
USER EXPERIENCE:
1. Admin logs in with default password (asteroid123)
2. Sees 'Account Security' section at top of dashboard
3. Fills in current password and new password
4. Clicks 'Change Password'
5. Gets immediate feedback
6. Password is changed - use new password on next login
This makes it easy for admins to change the default password
without needing REPL access or curl commands.
Ref: TODO.org Problem 4 - Security improvements
NEW API ENDPOINTS:
1. /api/asteroid/user/change-password (authenticated users)
- Users can change their own password
- Requires current password verification
- Returns 401 if current password is incorrect
- Returns 200 on success
2. /api/asteroid/admin/reset-password (admin only)
- Admins can reset any user's password
- No current password required
- Returns 404 if user not found
- Returns 200 on success
USAGE EXAMPLES:
User changes own password:
curl -X POST http://localhost:8080/api/asteroid/user/change-password \
-d 'current-password=asteroid123&new-password=newsecurepass' \
-b cookies.txt
Admin resets user password:
curl -X POST http://localhost:8080/api/asteroid/admin/reset-password \
-d 'username=admin&new-password=newsecurepass' \
-b cookies.txt
This addresses the security concern about the default admin password.
Admins can now reset it via API without needing REPL access.
Ref: TODO.org Problem 4 - Default admin password
SECURITY IMPROVEMENTS:
- Liquidsoap telnet now binds to 127.0.0.1 (was 0.0.0.0)
- Only accessible from within the container itself
- Defense in depth: even if another container is compromised
- Liquidsoap now uses ICECAST_SOURCE_PASSWORD environment variable
- Reads from environment.get("ICECAST_SOURCE_PASSWORD")
- Falls back to default for development
- All three streams (MP3, AAC, Low-quality) use same variable
DOCUMENTATION:
- Added comment to icecast.xml explaining environment variable override
- Clarifies that docker-compose.yml environment variables take precedence
This completes the password externalization for all Docker services:
- Icecast: Uses env vars (already done)
- Liquidsoap: Now uses env vars ✅
- PostgreSQL: Uses env vars (already done)
Ref: TODO.org security improvements
CRITICAL SECURITY FIX:
- Remove display of default admin username and password from login.ctml
- Login page no longer advertises 'admin' / 'asteroid123' credentials
This was the security issue Fade mentioned during b612 deployment:
'the templates with the default passwords for sure need changing'
Addresses TODO item:
- Problem 4: Templates advertising default admin password ✅ FIXED
Ref: TODO.org line 29-30
CRITICAL SECURITY FIXES:
- Bind all Docker services to localhost only (127.0.0.1)
- Prevents external access to Liquidsoap telnet (port 1234)
- Prevents direct Icecast access without HAproxy (port 8000)
- Secures PostgreSQL port (5432)
DOCKER CHANGES (docker-compose.yml):
- Icecast: 127.0.0.1:8000:8000 (was 8000:8000)
- Liquidsoap: 127.0.0.1🔢1234 (was 1234:1234)
- PostgreSQL: 127.0.0.1:5432:5432 (was 5432:5432)
- All passwords now use environment variables
CONFIG TEMPLATE:
- Added ICECAST_SOURCE_PASSWORD
- Added ICECAST_RELAY_PASSWORD
- Documented all Docker password variables
Addresses TODO items from b612.asteroid.radio deployment:
- Problem 1: Liquidsoap telnet exposed ✅ FIXED
- Problem 2: Icecast binding to 0.0.0.0 ✅ FIXED
This prevents the security issues that forced Fade to shut down
the production server. Services are now only accessible via
HAproxy on the host machine.
Ref: TODO.org lines 25-27
SECURITY FIXES:
- Remove hardcoded Icecast admin password from codebase
- Implement environment-based configuration system
- Add configuration validation and warnings
NEW FILES:
- config.lisp: Centralized configuration management
- config.template.env: Documented configuration template
- SECURITY-CONFIG-CHANGES.org: Complete change documentation
CHANGES:
- asteroid.asd: Add config.lisp to system
- asteroid.lisp: Replace defparameter with config system
- frontend-partials.lisp: Use config for Icecast credentials
Addresses TODO items:
- Problem 4: Templates no longer advertise default passwords
- Server runtime configuration: All config parameterized
Breaking change: Production deployments MUST set ICECAST_ADMIN_PASSWORD
via environment variable.
Tested on b612.asteroid.radio production server - configuration system
works correctly with environment variables.
Ref: TODO.org lines 24-43
This commit implements three major refactorings to make the codebase more
idiomatic and maintainable:
1. Template Path Centralization
- Add *template-directory* parameter and helper functions
- Replace 11+ instances of repetitive template loading boilerplate
- New functions: template-path, load-template in template-utils.lisp
2. String Construction with FORMAT
- Replace concatenate with format for external URLs (Icecast, static files)
- Maintain Radiance URI handling for internal routes
- Applied to stream URLs, status endpoints, and API responses
3. Error Handling with Custom Conditions
- NEW FILE: conditions.lisp with comprehensive error hierarchy
- Custom conditions: not-found-error, authentication-error,
authorization-error, validation-error, database-error, asteroid-stream-error
- Helper macros: with-error-handling, with-db-error-handling
- Helper functions: signal-not-found, signal-validation-error, etc.
- Refactored 19 API endpoints and page routes
- Proper HTTP status codes: 404, 401, 403, 400, 500
Changes:
- conditions.lisp: NEW (180+ lines of error handling infrastructure)
- asteroid.asd: Add conditions.lisp to system components
- asteroid.lisp: Refactor 30+ endpoints, eliminate 200+ lines of boilerplate
- template-utils.lisp: Add centralized template loading helpers
- frontend-partials.lisp: Update template loading and string construction
Net result: -97 lines of code, significantly improved error handling,
more maintainable and idiomatic Common Lisp.
All changes tested and verified:
- Clean build
- All endpoints functional
- Error handling returns proper HTTP codes
- No regressions
Added Phase 8: Docker Deployment & Documentation (Oct 26 - Nov 1, 2025)
- easilok's Docker containerization work (user init, env vars, Dockerfile)
- Complete Docker deployment documentation
- Documentation updates and refinements
- Cross-distribution package manager support
Updated statistics:
- 213+ total commits (was 205+)
- 2.75 months active development (was 2.5)
- Luis Pereira (easilok) now at 23+ commits
Updated current state to November 2025:
- Complete Docker deployment for streams and application
- Comprehensive documentation overhaul
- Recent achievements section added
- Future work includes live chat and song requests per design.org
Last updated: 2025-11-01
- Updated DEVELOPMENT.org: music directory is now asteroid/music/ (not docker/music/)
- Clarified music/ can be a symlink to actual music collection
- Added multiple symlink options for music management
- Removed redundant Python integration examples from API-ENDPOINTS.org
- Removed duplicate Integration Examples section (curl already covered in Testing)
- Added package manager notes to INSTALLATION, DEVELOPMENT, DOCKER-STREAMING, and TESTING
- Notes clarify apt examples can be replaced with dnf, pacman, zypper, apk, etc.
- Maintains clean documentation without cluttering every command
- Created PROJECT-HISTORY.org with complete development timeline
- Updated all documentation dates to 2025-10-26
- Added current features: multiple player modes, stream queue control, dynamic URLs
- Updated repository URLs from placeholders to actual GitHub links
- Refreshed feature lists across all docs to reflect current state
- Added PostgreSQL status (configured, ready for migration)
- Updated root README.org with comprehensive current information
- Improved quick start guides and access points
- Enhanced API documentation with complete endpoint list
- Updated all streaming documentation for Docker setup
- Standardized author attribution across all docs
- Incremented docs version to 3.0
All documentation now accurately reflects the current state of the project
with 205+ commits, 3 core contributors, and 2.5 months of active development.
- Changed font-family from 'Courier New' to 'VT323' in body style
- Updated quality selector dropdown font to VT323
- Updated disable button inline style to use VT323
- Ensures consistent typography across the entire site
- Popout player already inherits VT323 from main stylesheet
- Add frameset mode with persistent audio player in bottom frame
- Add localStorage preference system for user choice
- Update all page navigation to work in both regular and frameset modes
- Add enable/disable buttons for frameset mode
- Fix redirect loops and template parameter issues
- Add pop-out player window (400x300px) with auto-reconnect on stream errors
- Add queue reordering with up/down buttons in admin panel
- Add 'Load Queue from M3U' functionality
- Remove Play/Stream buttons from track management
- Fix Liquidsoap audio quality issues:
- Remove ReplayGain and compression to prevent pulsing
- Change reload_mode to 'seconds' to prevent playlist exhaustion
- Reduce crossfade to 3 seconds
- Add audio buffering settings for stability
- Add auto-reconnect logic for both front page and pop-out players
- Add frameset mode with persistent audio player in bottom frame
- Add localStorage preference system for user choice
- Update all page navigation to work in both regular and frameset modes
- Add enable/disable buttons for frameset mode
- Fix redirect loops and template parameter issues
- Add pop-out player window (400x300px) with auto-reconnect on stream errors
- Add queue reordering with up/down buttons in admin panel
- Add 'Load Queue from M3U' functionality
- Remove Play/Stream buttons from track management
- Fix Liquidsoap audio quality issues:
- Remove ReplayGain and compression to prevent pulsing
- Change reload_mode to 'seconds' to prevent playlist exhaustion
- Reduce crossfade to 3 seconds
- Add audio buffering settings for stability
- Add auto-reconnect logic for both front page and pop-out players
- Add missing query variable in filterTracks() function
- Reads value from track-search input field
- Bug was pre-existing, not introduced by refactoring
- Add /api/asteroid/partial/now-playing-inline endpoint for inline text
- Refactor admin.js to use server-side partial (removes 23 lines of JSON/XML parsing)
- Fix hardcoded path in convert-to-docker-path to use *music-library-path* variable
- Consistent with front-page and player refactoring from upstream
- Improves portability and reduces client-side JavaScript complexity
- Add 5-second crossfades between tracks
- Use ReplayGain for consistent volume (removed normalize())
- Add audio compression to prevent clipping
- Liquidsoap watches playlist file and reloads every 5 seconds
- Fallback to random playback when queue is empty
- Fix playlist to play all tracks in order
- Queue management section with add/remove/clear controls
- Add to Queue button on each track in library browser
- Search tracks and add to queue
- Add 10 random tracks button
- Live stream monitor with Now Playing display
- Toast notifications for user feedback
- Real-time queue updates
Comprehensive documentation of:
- Session objectives and accomplishments
- All files modified with detailed changes
- Technical implementation details
- Testing results
- Integration notes for team members
Feature fully implemented and tested:
- Admin login redirects to /asteroid/admin
- Regular user login redirects to /asteroid/profile
- Front page nav links conditional on auth status and role
- Session persistence working across navigation
- User management fully functional
- Profile page API endpoints implemented
- Fix api-output wrapper handling in all JavaScript files
- Add profile page API endpoints (profile, listening-stats, recent-tracks, top-artists)
- Fix session persistence - auth-ui.js now correctly detects login status
- Fix user stats display - now shows correct counts (3 users, 1 admin)
- Fix View All Users table - properly displays all users
- Handle empty arrays gracefully in profile.js (no errors for missing data)
All UI issues resolved:
✓ User management page fully functional
✓ Session persists across navigation
✓ Profile page loads without errors
✓ Correct nav links shown based on role
✓ Admin sees Admin link, regular users don't
Core Features:
- Login redirects based on user role (admin -> /admin, users -> /profile)
- User registration redirects to /profile page
- Convert user management APIs to use define-api (Radiance standard)
- Add user statistics API endpoint
- Add create user API endpoint
- Add list users API endpoint
Authentication & Authorization:
- Update require-role to return proper JSON for API requests
- Fix password verification with debug logging
- Add reset-user-password function for admin use
API Endpoints (using define-api):
- /api/asteroid/users - Get all users (admin only)
- /api/asteroid/user-stats - Get user statistics (admin only)
- /api/asteroid/users/create - Create new user (admin only)
Bug Fixes:
- Fix JavaScript API path for user-stats endpoint
- Remove dependency on non-existent radiance:api-output
- Use api-output for proper JSON responses
Testing:
- Admin login redirects to /asteroid/admin ✓
- Regular user login redirects to /asteroid/profile ✓
- User creation working (testuser created successfully) ✓
- User statistics loading correctly ✓
Known Issues (non-blocking):
- User table display needs UI fixes
- Profile page needs additional API endpoints
- Session persistence on navigation needs investigation