Migrate from Hunchentoot to RADIANCE framework
Major Changes: - Replace Hunchentoot with RADIANCE web framework - Add Shirakumo distribution for RADIANCE access - Implement proper RADIANCE module with define-module declaration - Convert all route handlers to use define-page syntax - Update route paths for subdomain routing (asteroid.localhost:8080) - Fix API endpoint to use define-page instead of define-api - Update server management to use radiance:startup/shutdown Technical Improvements: - Modular architecture with subdomain routing - Proper RADIANCE module integration - Updated documentation with migration details - Fixed route syntax and parentheses issues - Added comprehensive server startup commands Routes now accessible at: - Main: http://asteroid.localhost:8080/ - Admin: http://asteroid.localhost:8080/admin - Player: http://asteroid.localhost:8080/player - API: http://asteroid.localhost:8080/api/status
This commit is contained in:
parent
4f399b95fa
commit
8298dfed4c
|
|
@ -6,7 +6,7 @@
|
||||||
:author "Brian O'Reilly <fade@deepsky.com>"
|
:author "Brian O'Reilly <fade@deepsky.com>"
|
||||||
:license "GNU AFFERO GENERAL PUBLIC LICENSE V.3"
|
:license "GNU AFFERO GENERAL PUBLIC LICENSE V.3"
|
||||||
:serial t
|
:serial t
|
||||||
:depends-on (:HUNCHENTOOT
|
:depends-on (:RADIANCE
|
||||||
:SPINNERET
|
:SPINNERET
|
||||||
:CL-JSON
|
:CL-JSON
|
||||||
)
|
)
|
||||||
|
|
|
||||||
137
asteroid.lisp
137
asteroid.lisp
|
|
@ -1,6 +1,6 @@
|
||||||
;; -*-lisp-*-
|
;; -*-lisp-*-
|
||||||
(defpackage :asteroid
|
(defpackage :asteroid
|
||||||
(:use :cl)
|
(:use :cl :radiance)
|
||||||
(:use :asteroid.app-utils)
|
(:use :asteroid.app-utils)
|
||||||
(:export :-main
|
(:export :-main
|
||||||
:start-server
|
:start-server
|
||||||
|
|
@ -9,19 +9,21 @@
|
||||||
|
|
||||||
(in-package :asteroid)
|
(in-package :asteroid)
|
||||||
|
|
||||||
|
;; Define as RADIANCE module
|
||||||
|
(define-module asteroid
|
||||||
|
(:use #:cl #:radiance)
|
||||||
|
(:domain "asteroid"))
|
||||||
|
|
||||||
;; Configuration
|
;; Configuration
|
||||||
(defparameter *server-port* 8080)
|
(defparameter *server-port* 8080)
|
||||||
(defparameter *server-host* "localhost")
|
|
||||||
(defparameter *acceptor* nil)
|
|
||||||
|
|
||||||
;; HTML generation helpers
|
;; RADIANCE route handlers
|
||||||
(defun generate-page-html (title &rest body-content)
|
(define-page index #@"/" ()
|
||||||
"Generate a complete HTML page with consistent styling"
|
|
||||||
(spinneret:with-html-string
|
(spinneret:with-html-string
|
||||||
(:doctype)
|
(:doctype)
|
||||||
(:html
|
(:html
|
||||||
(:head
|
(:head
|
||||||
(:title title)
|
(:title "🎵 ASTEROID RADIO 🎵")
|
||||||
(:meta :charset "utf-8")
|
(:meta :charset "utf-8")
|
||||||
(:meta :name "viewport" :content "width=device-width, initial-scale=1")
|
(:meta :name "viewport" :content "width=device-width, initial-scale=1")
|
||||||
(:style "
|
(:style "
|
||||||
|
|
@ -32,64 +34,35 @@
|
||||||
.status { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
.status { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
||||||
.panel { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
.panel { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
||||||
.nav { margin: 20px 0; }
|
.nav { margin: 20px 0; }
|
||||||
.nav a { color: #00ff00; text-decoration: none; margin-right: 20px; padding: 10px; border: 1px solid #333; }
|
.nav a { color: #00ff00; text-decoration: none; margin: 0 15px; padding: 10px 20px; border: 1px solid #333; background: #1a1a1a; display: inline-block; }
|
||||||
.nav a:hover { background: #333; }
|
|
||||||
.back { color: #00ff00; text-decoration: none; }
|
|
||||||
button { background: #333; color: #00ff00; border: 1px solid #555; padding: 10px 20px; margin: 5px; cursor: pointer; }
|
|
||||||
button:hover { background: #555; }
|
|
||||||
.player { background: #1a1a1a; padding: 40px; border: 1px solid #333; margin: 40px auto; max-width: 600px; text-align: center; }
|
|
||||||
.now-playing { font-size: 1.5em; margin: 20px 0; color: #ff6600; }
|
|
||||||
.controls button { padding: 15px 30px; margin: 10px; font-size: 1.2em; }
|
|
||||||
"))
|
|
||||||
(:body
|
|
||||||
(:div.container
|
|
||||||
(mapcar (lambda (element) element) body-content))))))
|
|
||||||
|
|
||||||
;; Route handlers
|
|
||||||
(defun handle-index ()
|
|
||||||
"Main page handler"
|
|
||||||
(spinneret:with-html-string
|
|
||||||
(:doctype)
|
|
||||||
(:html
|
|
||||||
(:head
|
|
||||||
(:title "Asteroid Radio - Music for Hackers")
|
|
||||||
(:meta :charset "utf-8")
|
|
||||||
(:meta :name "viewport" :content "width=device-width, initial-scale=1")
|
|
||||||
(:style "
|
|
||||||
body { font-family: 'Courier New', monospace; background: #0a0a0a; color: #00ff00; margin: 0; padding: 20px; }
|
|
||||||
.container { max-width: 1200px; margin: 0 auto; }
|
|
||||||
h1 { color: #ff6600; text-align: center; font-size: 2.5em; margin-bottom: 30px; }
|
|
||||||
h2 { color: #ff6600; }
|
|
||||||
.status { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
|
||||||
.nav { margin: 20px 0; }
|
|
||||||
.nav a { color: #00ff00; text-decoration: none; margin-right: 20px; padding: 10px; border: 1px solid #333; }
|
|
||||||
.nav a:hover { background: #333; }
|
.nav a:hover { background: #333; }
|
||||||
|
.controls { margin: 20px 0; }
|
||||||
|
.controls button { background: #1a1a1a; color: #00ff00; border: 1px solid #333; padding: 10px 20px; margin: 5px; cursor: pointer; }
|
||||||
|
.controls button:hover { background: #333; }
|
||||||
|
.now-playing { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
|
||||||
|
.back { color: #00ff00; text-decoration: none; margin-bottom: 20px; display: inline-block; }
|
||||||
|
.back:hover { text-decoration: underline; }
|
||||||
"))
|
"))
|
||||||
(:body
|
(:body
|
||||||
(:div.container
|
(:div.container
|
||||||
(:h1 "🎵 ASTEROID RADIO 🎵")
|
(:h1 "🎵 ASTEROID RADIO 🎵")
|
||||||
(:div.status
|
(:div.status
|
||||||
(:h2 "Station Status")
|
(:h2 "Station Status")
|
||||||
(:p "Status: " (:strong "Running"))
|
(:p "🟢 LIVE - Broadcasting asteroid music for hackers")
|
||||||
(:p "Now Playing: " (:em "Silence (for now)"))
|
(:p "Current listeners: 0")
|
||||||
(:p "Listeners: 0"))
|
(:p "Stream quality: 128kbps MP3"))
|
||||||
(:div.nav
|
(:div.nav
|
||||||
(:a :href "/admin" "Admin Dashboard")
|
(:a :href "/admin" "Admin Dashboard")
|
||||||
(:a :href "/player" "Web Player")
|
(:a :href "/player" "Web Player")
|
||||||
(:a :href "/api/status" "API Status"))
|
(:a :href "/api/status" "API Status"))
|
||||||
(:div
|
(:div
|
||||||
(:h2 "Welcome to Asteroid Radio")
|
(:h2 "Now Playing")
|
||||||
(:p "A streaming radio station for hackers, built with Common Lisp.")
|
(:p "Artist: The Void")
|
||||||
(:p "Features coming soon:")
|
(:p "Track: Silence")
|
||||||
(:ul
|
(:p "Album: Startup Sounds")
|
||||||
(:li "Auto-DJ with crossfading")
|
(:p "Duration: ∞")))))))
|
||||||
(:li "Live DJ handoff")
|
|
||||||
(:li "Song requests")
|
|
||||||
(:li "Admin dashboard")
|
|
||||||
(:li "Music library management"))))))))
|
|
||||||
|
|
||||||
(defun handle-admin ()
|
(define-page admin #@"/admin" ()
|
||||||
"Admin dashboard handler"
|
|
||||||
(spinneret:with-html-string
|
(spinneret:with-html-string
|
||||||
(:doctype)
|
(:doctype)
|
||||||
(:html
|
(:html
|
||||||
|
|
@ -132,8 +105,7 @@
|
||||||
(:p "Liquidsoap: Not Running")
|
(:p "Liquidsoap: Not Running")
|
||||||
(:p "Icecast: Not Running")))))))
|
(:p "Icecast: Not Running")))))))
|
||||||
|
|
||||||
(defun handle-player ()
|
(define-page player #@"/player" ()
|
||||||
"Web player handler"
|
|
||||||
(spinneret:with-html-string
|
(spinneret:with-html-string
|
||||||
(:doctype)
|
(:doctype)
|
||||||
(:html
|
(:html
|
||||||
|
|
@ -164,9 +136,8 @@
|
||||||
(:p "Bitrate: 128kbps MP3")
|
(:p "Bitrate: 128kbps MP3")
|
||||||
(:p "Status: Offline")))))))
|
(:p "Status: Offline")))))))
|
||||||
|
|
||||||
(defun handle-api-status ()
|
(define-page api/status #@"/api/status" ()
|
||||||
"API status endpoint handler"
|
(setf (radiance:header "Content-Type") "application/json")
|
||||||
(setf (hunchentoot:content-type*) "application/json")
|
|
||||||
(cl-json:encode-json-to-string
|
(cl-json:encode-json-to-string
|
||||||
`(("status" . "running")
|
`(("status" . "running")
|
||||||
("server" . "asteroid-radio")
|
("server" . "asteroid-radio")
|
||||||
|
|
@ -178,48 +149,22 @@
|
||||||
("listeners" . 0)
|
("listeners" . 0)
|
||||||
("stream-url" . "http://localhost:8000/asteroid"))))
|
("stream-url" . "http://localhost:8000/asteroid"))))
|
||||||
|
|
||||||
;; Route setup
|
;; RADIANCE server management functions
|
||||||
(defun setup-routes ()
|
(defun start-server (&key (port *server-port*))
|
||||||
"Set up all HTTP routes"
|
"Start the Asteroid Radio RADIANCE server"
|
||||||
(hunchentoot:define-easy-handler (index :uri "/") ()
|
(format t "Starting Asteroid Radio RADIANCE server on port ~a~%" port)
|
||||||
(handle-index))
|
(radiance:startup)
|
||||||
|
(format t "Server started! Visit http://localhost:~a/asteroid/~%" port))
|
||||||
(hunchentoot:define-easy-handler (admin :uri "/admin") ()
|
|
||||||
(handle-admin))
|
|
||||||
|
|
||||||
(hunchentoot:define-easy-handler (player :uri "/player") ()
|
|
||||||
(handle-player))
|
|
||||||
|
|
||||||
(hunchentoot:define-easy-handler (api-status :uri "/api/status") ()
|
|
||||||
(handle-api-status)))
|
|
||||||
|
|
||||||
;; Server management functions
|
|
||||||
(defun start-server (&key (port *server-port*) (host *server-host*))
|
|
||||||
"Start the Asteroid Radio web server"
|
|
||||||
(when *acceptor*
|
|
||||||
(hunchentoot:stop *acceptor*))
|
|
||||||
|
|
||||||
(format t "Setting up routes...~%")
|
|
||||||
(setup-routes)
|
|
||||||
|
|
||||||
(format t "Starting Asteroid Radio server on ~a:~a~%" host port)
|
|
||||||
(setf *acceptor* (make-instance 'hunchentoot:easy-acceptor
|
|
||||||
:port port
|
|
||||||
:address host))
|
|
||||||
(hunchentoot:start *acceptor*)
|
|
||||||
(format t "Server started! Visit http://~a:~a~%" host port))
|
|
||||||
|
|
||||||
(defun stop-server ()
|
(defun stop-server ()
|
||||||
"Stop the Asteroid Radio web server"
|
"Stop the Asteroid Radio RADIANCE server"
|
||||||
(when *acceptor*
|
(format t "Stopping Asteroid Radio server...~%")
|
||||||
(format t "Stopping Asteroid Radio server...~%")
|
(radiance:shutdown)
|
||||||
(hunchentoot:stop *acceptor*)
|
(format t "Server stopped.~%"))
|
||||||
(setf *acceptor* nil)
|
|
||||||
(format t "Server stopped.~%")))
|
|
||||||
|
|
||||||
(defun run-server (&key (port *server-port*) (host *server-host*))
|
(defun run-server (&key (port *server-port*))
|
||||||
"Start the server and keep it running (blocking)"
|
"Start the server and keep it running (blocking)"
|
||||||
(start-server :port port :host host)
|
(start-server :port port)
|
||||||
(format t "Server running. Press Ctrl+C to stop.~%")
|
(format t "Server running. Press Ctrl+C to stop.~%")
|
||||||
;; Keep the server running
|
;; Keep the server running
|
||||||
(handler-case
|
(handler-case
|
||||||
|
|
@ -231,6 +176,6 @@
|
||||||
(defun -main (&optional args)
|
(defun -main (&optional args)
|
||||||
(declare (ignore args))
|
(declare (ignore args))
|
||||||
(format t "~%🎵 ASTEROID RADIO - Music for Hackers 🎵~%")
|
(format t "~%🎵 ASTEROID RADIO - Music for Hackers 🎵~%")
|
||||||
(format t "Starting web server...~%")
|
(format t "Starting RADIANCE web server...~%")
|
||||||
(run-server))
|
(run-server))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,39 +32,42 @@ This document summarizes the implementation of a basic web server for the Astero
|
||||||
** Removed Dependencies
|
** Removed Dependencies
|
||||||
| Dependency | Reason for Removal |
|
| Dependency | Reason for Removal |
|
||||||
|------------|-------------------|
|
|------------|-------------------|
|
||||||
| :RADIANCE | Not available in Quicklisp distribution |
|
| :RADIANCE | Requires separate Shirakumo dist installation, switched to simpler Hunchentoot for MVP |
|
||||||
| :MITO | Not available in Quicklisp, not needed for basic web server |
|
| :MITO | Not available in default Quicklisp, not needed for basic web server |
|
||||||
| :MITO-AUTH | Not available in Quicklisp, authentication not needed for MVP |
|
| :MITO-AUTH | Not available in default Quicklisp, authentication not needed for MVP |
|
||||||
| :STR | Not used in current implementation |
|
| :STR | Not used in current implementation |
|
||||||
| :PZMQ | Not needed for basic web server functionality |
|
| :PZMQ | Not needed for basic web server functionality |
|
||||||
|
|
||||||
** Final Dependencies
|
** Final Dependencies
|
||||||
| Dependency | Purpose | Justification |
|
| Dependency | Purpose | Justification |
|
||||||
|------------|---------|---------------|
|
|------------|---------|---------------|
|
||||||
| :HUNCHENTOOT | Web server framework | Widely available, stable, well-documented Common Lisp web server |
|
| :RADIANCE | Web framework | Modular web framework with subdomain routing and integrated module system |
|
||||||
| :SPINNERET | HTML generation | Clean DSL for generating HTML, integrates well with Common Lisp |
|
| :SPINNERET | HTML generation | Clean DSL for generating HTML, integrates well with Common Lisp |
|
||||||
| :CL-JSON | JSON encoding/decoding | Standard library for API endpoints |
|
| :CL-JSON | JSON encoding/decoding | Standard library for API endpoints |
|
||||||
|
|
||||||
** Why Hunchentoot Over RADIANCE
|
** Migration from Hunchentoot to RADIANCE
|
||||||
- **Availability**: RADIANCE not found in Quicklisp distribution
|
- **Initial Choice**: Started with Hunchentoot for simpler MVP setup
|
||||||
- **Stability**: Hunchentoot is mature, battle-tested web server
|
- **Discovery**: RADIANCE available via Shirakumo dist: `(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")`
|
||||||
- **Documentation**: Extensive documentation and community support
|
- **Migration Completed**: Successfully migrated to RADIANCE framework
|
||||||
- **Simplicity**: Easier to set up for basic web server needs
|
- **Benefits**: RADIANCE provides modular architecture, subdomain routing, and integrated module system
|
||||||
|
- **Result**: More scalable foundation for future radio station features
|
||||||
|
|
||||||
* Implementation Details
|
* Implementation Details
|
||||||
|
|
||||||
** Web Server Architecture
|
** Web Server Architecture
|
||||||
- Port: 8080 (configurable via =*server-port*=)
|
- Framework: RADIANCE modular web framework
|
||||||
- Host: localhost (configurable via =*server-host*=)
|
- Port: 8080 (default RADIANCE configuration)
|
||||||
- Server management: =*acceptor*= global variable tracks server instance
|
- Module: asteroid (domain: "asteroid")
|
||||||
|
- Subdomain routing: asteroid.localhost:8080
|
||||||
|
- Server management: =radiance:startup= / =radiance:shutdown=
|
||||||
|
|
||||||
** Route Structure
|
** Route Structure (RADIANCE)
|
||||||
| Route | Handler | Purpose |
|
| Route | Handler | Purpose | URL |
|
||||||
|-------|---------|---------|
|
|-------|---------|---------|-----|
|
||||||
| / | =handle-index= | Main page with station status |
|
| / | =index= | Main page with station status | http://asteroid.localhost:8080/ |
|
||||||
| /admin | =handle-admin= | Admin dashboard with controls |
|
| /admin | =admin= | Admin dashboard with controls | http://asteroid.localhost:8080/admin |
|
||||||
| /player | =handle-player= | Web player interface |
|
| /player | =player= | Web player interface | http://asteroid.localhost:8080/player |
|
||||||
| /api/status | =handle-api-status= | JSON API endpoint |
|
| /api/status | =api/status= | JSON API endpoint | http://asteroid.localhost:8080/api/status |
|
||||||
|
|
||||||
** HTML Generation Strategy
|
** HTML Generation Strategy
|
||||||
- Direct use of =spinneret:with-html-string= in each handler
|
- Direct use of =spinneret:with-html-string= in each handler
|
||||||
|
|
@ -88,14 +91,19 @@ Removed unused dependencies from =asteroid.asd=:
|
||||||
- Removed :MITO, :MITO-AUTH, :STR, :PZMQ
|
- Removed :MITO, :MITO-AUTH, :STR, :PZMQ
|
||||||
- Kept only essential dependencies: :HUNCHENTOOT, :SPINNERET, :CL-JSON
|
- Kept only essential dependencies: :HUNCHENTOOT, :SPINNERET, :CL-JSON
|
||||||
|
|
||||||
** Error 2: RADIANCE Framework Unavailable
|
** Error 2: RADIANCE Framework Migration
|
||||||
*** Problem
|
*** Problem
|
||||||
Original design assumed RADIANCE web framework, but not available in Quicklisp.
|
Initially avoided RADIANCE due to perceived unavailability, implemented with Hunchentoot instead.
|
||||||
|
|
||||||
|
*** Root Cause
|
||||||
|
RADIANCE available via Shirakumo dist: `(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")` but required additional setup step.
|
||||||
|
|
||||||
*** Solution
|
*** Solution
|
||||||
- Replaced RADIANCE with Hunchentoot
|
- Successfully migrated from Hunchentoot to RADIANCE
|
||||||
- Rewrote web server initialization and route handling
|
- Installed Shirakumo distribution for RADIANCE access
|
||||||
- Used =hunchentoot:define-easy-handler= for route definitions
|
- Rewrote route handlers using =define-page= syntax
|
||||||
|
- Added =define-module= declaration for proper RADIANCE integration
|
||||||
|
- Updated server management to use =radiance:startup= / =radiance:shutdown=
|
||||||
|
|
||||||
** Error 3: HTML Generation Function Signature Mismatch
|
** Error 3: HTML Generation Function Signature Mismatch
|
||||||
*** Problem
|
*** Problem
|
||||||
|
|
@ -109,21 +117,23 @@ Initial =generate-page-html= helper function designed for single body argument,
|
||||||
*** Solution
|
*** Solution
|
||||||
Attempted fix with =&rest= parameter, but Spinneret macro expansion issues persisted.
|
Attempted fix with =&rest= parameter, but Spinneret macro expansion issues persisted.
|
||||||
|
|
||||||
** Error 4: Spinneret Macro Expansion Issues
|
** Error 4: RADIANCE Route Syntax Issues
|
||||||
*** Problem
|
*** Problem
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
[ERROR] The function :H1 is undefined.
|
Module #<PACKAGE "ASTEROID"> requested but while the package exists, it is not a module.
|
||||||
Internal Server Error in browser
|
The value #@"asteroid/api/status" is not of type LIST
|
||||||
#+END_EXAMPLE
|
#+END_EXAMPLE
|
||||||
|
|
||||||
*** Root Cause
|
*** Root Cause
|
||||||
Complex helper function approach interfered with Spinneret's macro expansion system.
|
- Missing =define-module= declaration for RADIANCE integration
|
||||||
|
- Incorrect route path syntax using =asteroid/= prefix instead of module-relative paths
|
||||||
|
- Wrong API endpoint definition syntax
|
||||||
|
|
||||||
*** Solution
|
*** Solution
|
||||||
- Abandoned helper function approach
|
- Added =define-module= declaration with proper domain specification
|
||||||
- Rewrote each handler to use =spinneret:with-html-string= directly
|
- Fixed route paths: =#@"asteroid/"= → =#@"/"=, =#@"asteroid/admin"= → =#@"/admin"=
|
||||||
- Embedded CSS styling directly in each page
|
- Updated API endpoint to use =define-page= instead of =define-api=
|
||||||
- Simplified HTML generation to work within Spinneret's macro system
|
- Fixed parentheses syntax errors in HTML generation
|
||||||
|
|
||||||
** Error 5: Shell History Expansion Issues
|
** Error 5: Shell History Expansion Issues
|
||||||
*** Problem
|
*** Problem
|
||||||
|
|
@ -160,31 +170,43 @@ Used single quotes instead of double quotes for SBCL command-line arguments to p
|
||||||
|
|
||||||
** 🚀 Running the Server
|
** 🚀 Running the Server
|
||||||
|
|
||||||
|
*** RADIANCE Setup (One-time)
|
||||||
|
#+BEGIN_EXAMPLE
|
||||||
|
sbcl --eval '(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")'
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
*** Command Line (One-shot execution)
|
*** Command Line (One-shot execution)
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
sbcl --eval '(ql:quickload (quote (:hunchentoot :spinneret :cl-json)))' \
|
sbcl --eval '(ql:quickload (quote (:radiance :spinneret :cl-json)))' \
|
||||||
--eval '(load "asteroid.asd")' \
|
--eval '(load "asteroid.asd")' \
|
||||||
--eval '(asdf:load-system :asteroid)' \
|
--eval '(asdf:load-system :asteroid)' \
|
||||||
--eval '(asteroid:start-server)' \
|
--eval '(asteroid:start-server)' \
|
||||||
--eval '(format t "Server running at http://localhost:8080 - Press Ctrl+C to stop")'
|
--eval '(format t "Server running at http://asteroid.localhost:8080/ - Press Ctrl+C to stop")'
|
||||||
#+END_EXAMPLE
|
#+END_EXAMPLE
|
||||||
|
|
||||||
*** Interactive REPL
|
*** Interactive REPL
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
sbcl
|
sbcl
|
||||||
(ql:quickload '(:hunchentoot :spinneret :cl-json))
|
(ql:quickload '(:radiance :spinneret :cl-json))
|
||||||
(load "asteroid.asd")
|
(load "asteroid.asd")
|
||||||
(asdf:load-system :asteroid)
|
(asdf:load-system :asteroid)
|
||||||
(asteroid:start-server)
|
(asteroid:start-server)
|
||||||
;; Server now running at http://localhost:8080
|
;; Server now running at http://asteroid.localhost:8080/
|
||||||
;; To stop: (asteroid:stop-server)
|
;; To stop: (asteroid:stop-server)
|
||||||
#+END_EXAMPLE
|
#+END_EXAMPLE
|
||||||
|
|
||||||
*** Available Functions
|
*** Available Functions
|
||||||
- =(asteroid:start-server)= - Start web server (non-blocking)
|
- =(asteroid:start-server)= - Start RADIANCE server (non-blocking)
|
||||||
- =(asteroid:stop-server)= - Stop web server cleanly
|
- =(asteroid:stop-server)= - Stop RADIANCE server cleanly
|
||||||
- =(asteroid:run-server)= - Start server and keep running (blocking, with Ctrl+C handler)
|
- =(asteroid:run-server)= - Start server and keep running (blocking, with Ctrl+C handler)
|
||||||
|
|
||||||
|
*** Access URLs
|
||||||
|
- **Main page**: http://asteroid.localhost:8080/
|
||||||
|
- **Admin dashboard**: http://asteroid.localhost:8080/admin
|
||||||
|
- **Web player**: http://asteroid.localhost:8080/player
|
||||||
|
- **API endpoint**: http://asteroid.localhost:8080/api/status
|
||||||
|
- **RADIANCE welcome**: http://localhost:8080/
|
||||||
|
|
||||||
** 📋 Next Steps (Not Implemented)
|
** 📋 Next Steps (Not Implemented)
|
||||||
- Database integration (when MITO alternative chosen)
|
- Database integration (when MITO alternative chosen)
|
||||||
- Audio streaming backend (Liquidsoap integration)
|
- Audio streaming backend (Liquidsoap integration)
|
||||||
|
|
@ -193,6 +215,7 @@ sbcl
|
||||||
- Authentication system
|
- Authentication system
|
||||||
- Real-time now-playing updates
|
- Real-time now-playing updates
|
||||||
- WebSocket integration for live updates
|
- WebSocket integration for live updates
|
||||||
|
- **Completed**: ✅ Successfully migrated to RADIANCE framework
|
||||||
|
|
||||||
* Technical Lessons Learned
|
* Technical Lessons Learned
|
||||||
|
|
||||||
|
|
@ -202,9 +225,11 @@ sbcl
|
||||||
- Keep dependency list minimal for initial implementation
|
- Keep dependency list minimal for initial implementation
|
||||||
|
|
||||||
** Common Lisp Web Development
|
** Common Lisp Web Development
|
||||||
- Hunchentoot provides robust foundation for web applications
|
- RADIANCE provides modular architecture with subdomain routing
|
||||||
- Spinneret works best with direct macro usage, not through helper functions
|
- Spinneret works best with direct macro usage, not through helper functions
|
||||||
- HTML generation should be kept simple and direct
|
- HTML generation should be kept simple and direct
|
||||||
|
- RADIANCE modules require proper =define-module= declarations
|
||||||
|
- Route paths in RADIANCE are module-relative (use =#@"/"= not =#@"asteroid/"=)
|
||||||
|
|
||||||
** Error Handling Strategy
|
** Error Handling Strategy
|
||||||
- Compilation warnings often indicate runtime issues
|
- Compilation warnings often indicate runtime issues
|
||||||
|
|
@ -233,6 +258,12 @@ asteroid/
|
||||||
|
|
||||||
* Conclusion
|
* Conclusion
|
||||||
|
|
||||||
Successfully implemented a functional web server foundation for the Asteroid Radio project. The server provides a complete web interface with admin controls, player interface, and API endpoints. Key success factors included pragmatic dependency choices, incremental development approach, and thorough error resolution.
|
Successfully implemented and migrated a functional web server foundation for the Asteroid Radio project using RADIANCE framework. The server provides a complete web interface with admin controls, player interface, and API endpoints accessible via subdomain routing at asteroid.localhost:8080.
|
||||||
|
|
||||||
The implementation is ready for the next development phase: integrating audio streaming components and database functionality.
|
Key achievements:
|
||||||
|
- **Framework Migration**: Successfully migrated from Hunchentoot to RADIANCE
|
||||||
|
- **Modular Architecture**: Implemented proper RADIANCE module with subdomain routing
|
||||||
|
- **Complete Web Interface**: Main page, admin dashboard, web player, and JSON API
|
||||||
|
- **Scalable Foundation**: RADIANCE provides better architecture for future radio features
|
||||||
|
|
||||||
|
The implementation demonstrates the value of exploring framework alternatives and provides a robust, modular foundation ready for the next development phase: integrating audio streaming components (Liquidsoap/Icecast) and database functionality.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue