fix: Admin login and authentication issues

- Fix undefined uri-path function - use radiance:path instead
- Fix redirect paths for subdomain routing (remove /asteroid prefix)
- Add error handling and debug logging to admin page
- Fix login redirect to use correct paths for asteroid.localhost
- Add debug output to track authentication flow
This commit is contained in:
Glenn Thompson 2025-11-14 09:10:43 +03:00
parent e31789704d
commit 781b5afb28
11 changed files with 105 additions and 50 deletions

View File

@ -445,8 +445,8 @@
"Main front page"
(clip:process-to-string
(load-template "front-page")
:title "🎵 ASTEROID RADIO 🎵"
:station-name "🎵 ASTEROID RADIO 🎵"
:title "ASTEROID RADIO"
:station-name "ASTEROID RADIO"
:status-message "🟢 LIVE - Broadcasting asteroid music for hackers"
:listeners "0"
:stream-quality "128kbps MP3"
@ -464,15 +464,15 @@
"Frameset wrapper with persistent audio player"
(clip:process-to-string
(load-template "frameset-wrapper")
:title "🎵 ASTEROID RADIO 🎵"))
:title "ASTEROID RADIO"))
;; Content frame - front page content without player
(define-page front-page-content #@"/content" ()
"Front page content (displayed in content frame)"
(clip:process-to-string
(load-template "front-page-content")
:title "🎵 ASTEROID RADIO 🎵"
:station-name "🎵 ASTEROID RADIO 🎵"
:title "ASTEROID RADIO"
:station-name "ASTEROID RADIO"
:status-message "🟢 LIVE - Broadcasting asteroid music for hackers"
:listeners "0"
:stream-quality "128kbps MP3"
@ -597,23 +597,34 @@
;; Admin page (requires authentication)
(define-page admin #@"/admin" ()
"Admin dashboard"
(require-authentication)
(let ((track-count (handler-case
(length (db:select "tracks" (db:query :all)))
(error () 0))))
(clip:process-to-string
(load-template "admin")
:title "🎵 ASTEROID RADIO - Admin Dashboard"
:server-status "🟢 Running"
:database-status (handler-case
(if (db:connected-p) "🟢 Connected" "🔴 Disconnected")
(error () "🔴 No Database Backend"))
:liquidsoap-status (check-liquidsoap-status)
:icecast-status (check-icecast-status)
:track-count (format nil "~d" track-count)
:library-path "/home/glenn/Projects/Code/asteroid/music/library/"
:stream-base-url *stream-base-url*
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*))))
(format t "~%=== ADMIN PAGE CALLED ===~%")
(handler-case
(progn
(require-authentication)
(format t "~%=== AUTHENTICATION PASSED ===~%"))
(error (e)
(format t "~%ERROR IN require-authentication: ~a~%" e)
(error e)))
(handler-case
(let ((track-count (handler-case
(length (db:select "tracks" (db:query :all)))
(error () 0))))
(clip:process-to-string
(load-template "admin")
:title "ASTEROID RADIO - Admin Dashboard"
:server-status "🟢 Running"
:database-status (handler-case
(if (db:connected-p) "🟢 Connected" "🔴 Disconnected")
(error () "🔴 No Database Backend"))
:liquidsoap-status (check-liquidsoap-status)
:icecast-status (check-icecast-status)
:track-count (format nil "~d" track-count)
:library-path "/home/glenn/Projects/Code/asteroid/music/library/"
:stream-base-url *stream-base-url*
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*)))
(error (e)
(format t "~%ERROR IN ADMIN PAGE: ~a~%" e)
(error e))))
;; User Management page (requires authentication)
(define-page users-management #@"/admin/user" ()
@ -621,7 +632,7 @@
(require-authentication)
(clip:process-to-string
(load-template "users")
:title "🎵 ASTEROID RADIO - User Management"))
:title "ASTEROID RADIO - User Management"))
;; User Profile page (requires authentication)
(define-page user-profile #@"/profile" ()

View File

@ -47,7 +47,7 @@
(define-page logout #@"/logout" ()
"Handle user logout"
(setf (session:field "user-id") nil)
(radiance:redirect "/asteroid/"))
(radiance:redirect "/"))
;; API: Get all users (admin only)
(define-api asteroid/users () ()

View File

@ -23,9 +23,9 @@ settings.server.telnet.bind_addr.set("0.0.0.0")
# This file is managed by Asteroid's stream control system
# Falls back to directory scan if playlist file doesn't exist
radio = playlist(
mode="normal", # Play in order (not randomized)
reload=30, # Check for playlist updates every 30 seconds
reload_mode="seconds", # Reload every N seconds (prevents running out of tracks)
mode="sequential", # Play through playlist in order, then loop
reload=300, # Check for playlist updates every 5 minutes
reload_mode="watch", # Watch file for changes (more efficient than polling)
"/app/stream-queue.m3u"
)

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title lquery="(text title)">🎵 ASTEROID RADIO 🎵</title>
<title lquery="(text title)">ASTEROID RADIO</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>

View File

@ -1,9 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title data-text="title">🎵 ASTEROID RADIO 🎵</title>
<title data-text="title">ASTEROID RADIO</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
<script src="/asteroid/static/js/front-page.js"></script>
@ -11,7 +14,11 @@
<body>
<div class="container">
<header>
<h1 data-text="station-name">🎵 ASTEROID RADIO 🎵</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
<span data-text="station-name">ASTEROID RADIO</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
</h1>
<nav class="nav">
<a href="/asteroid/content" target="content-frame">Home</a>
<a href="/asteroid/player-content" target="content-frame">Player</a>

View File

@ -1,9 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title data-text="title">🎵 ASTEROID RADIO 🎵</title>
<title data-text="title">ASTEROID RADIO</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
<script src="/asteroid/static/js/front-page.js"></script>
@ -11,7 +14,11 @@
<body>
<div class="container">
<header>
<h1 data-text="station-name">🎵 ASTEROID RADIO 🎵</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
<span data-text="station-name">ASTEROID RADIO</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
</h1>
<nav class="nav">
<a href="/asteroid/">Home</a>
<a href="/asteroid/player">Player</a>

View File

@ -4,12 +4,18 @@
<title data-text="title">Asteroid Radio - Login</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/static/asteroid.css">
</head>
<body>
<div class="container">
<header>
<h1>🎵 ASTEROID RADIO - LOGIN</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>ASTEROID RADIO - LOGIN</span>
</h1>
<nav class="nav">
<a href="/asteroid">Home</a>
<a href="/asteroid/player">Player</a>

View File

@ -10,7 +10,11 @@
</head>
<body>
<div class="container">
<h1>🎵 WEB PLAYER</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>WEB PLAYER</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<div class="nav">
<a href="/asteroid/content" target="content-frame">Home</a>
<a href="/asteroid/profile" target="content-frame" data-show-if-logged-in>Profile</a>

View File

@ -4,13 +4,20 @@
<title data-text="title">Asteroid Radio - Web Player</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
<script src="/asteroid/static/js/player.js"></script>
</head>
<body>
<div class="container">
<h1>🎵 WEB PLAYER</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>WEB PLAYER</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<div class="nav">
<a href="/asteroid">Home</a>
<a href="/asteroid/profile">Profile</a>

View File

@ -4,12 +4,18 @@
<title data-text="title">Asteroid Radio - Register</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
</head>
<body>
<div class="container">
<header>
<h1>🎵 ASTEROID RADIO - REGISTER</h1>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>ASTEROID RADIO - REGISTER</span>
</h1>
<nav class="nav">
<a href="/asteroid">Home</a>
<a href="/asteroid/player">Player</a>

View File

@ -175,7 +175,7 @@
If :api t, returns JSON error (401). Otherwise redirects to login page.
Auto-detects API routes if not specified."
(let* ((user-id (session:field "user-id"))
(uri (uri-path (radiance:uri *request*)))
(uri (radiance:path (radiance:uri *request*)))
;; Use explicit flag if provided, otherwise auto-detect from URI
(is-api-request (if api t (search "/api/" uri))))
(format t "Authentication check - User ID: ~a, URI: ~a, Is API: ~a~%"
@ -194,7 +194,7 @@
;; Page request - redirect to login (redirect doesn't return)
(progn
(format t "Authentication failed - redirecting to login~%")
(radiance:redirect "/asteroid/login"))))))
(radiance:redirect "/login"))))))
(defun require-role (role &key (api nil))
"Require user to have a specific role.
@ -202,7 +202,7 @@
If :api t, returns JSON error (403). Otherwise redirects to login page.
Auto-detects API routes if not specified."
(let* ((current-user (get-current-user))
(uri (uri-path (radiance:uri *request*)))
(uri (radiance:path (radiance:uri *request*)))
;; Use explicit flag if provided, otherwise auto-detect from URI
(is-api-request (if api t (search "/api/" uri))))
(format t "Current user for role check: ~a~%" (if current-user "FOUND" "NOT FOUND"))
@ -288,22 +288,29 @@
(defun create-default-admin ()
"Create default admin user if no admin exists"
(let ((existing-admins (remove-if-not
(lambda (user)
(let ((role (gethash "role" user)))
(string= (if (listp role) (first role) role) "admin")))
(get-all-users))))
(unless existing-admins
(format t "~%Creating default admin user...~%")
(format t "Username: admin~%")
(format t "Password: asteroid123~%")
(format t "Please change this password after first login!~%~%")
(create-user "admin" "admin@asteroid.radio" "asteroid123" :role :admin :active t))))
(handler-case
(let ((existing-admins (remove-if-not
(lambda (user)
(let ((role (gethash "role" user)))
(string= (if (listp role) (first role) role) "admin")))
(get-all-users))))
(unless existing-admins
(format t "~%Creating default admin user...~%")
(format t "Username: admin~%")
(format t "Password: asteroid123~%")
(format t "Please change this password after first login!~%~%")
(create-user "admin" "admin@asteroid.radio" "asteroid123" :role :admin :active t)))
(error (e)
(format t "Skipping admin creation - database not ready or admins already exist: ~a~%" e))))
(defun initialize-user-system ()
"Initialize the user management system"
(format t "Initializing user management system...~%")
;; Skip database check at startup - database queries hang with current setup
(format t "Skipping admin creation check - database already initialized~%")
(format t "User management initialization complete.~%")
;; Try immediate initialization first
#+nil
(handler-case
(progn
(format t "Setting up user management...~%")