Compare commits
No commits in common. "61570fe2e67bae537a3bb434f04a94c468c78d81" and "f691a1edc8089d29407868715c526d1594821fad" have entirely different histories.
61570fe2e6
...
f691a1edc8
|
|
@ -196,27 +196,6 @@
|
||||||
("message" . "Playlist not found"))
|
("message" . "Playlist not found"))
|
||||||
:status 404)))))
|
:status 404)))))
|
||||||
|
|
||||||
;; Recently played tracks API endpoint
|
|
||||||
(define-api asteroid/recently-played () ()
|
|
||||||
"Get the last 3 played tracks with AllMusic links"
|
|
||||||
(with-error-handling
|
|
||||||
(let ((tracks (get-recently-played)))
|
|
||||||
(api-output `(("status" . "success")
|
|
||||||
("tracks" . ,(mapcar (lambda (track)
|
|
||||||
(let* ((title (getf track :title))
|
|
||||||
(timestamp (getf track :timestamp))
|
|
||||||
(unix-timestamp (universal-time-to-unix timestamp))
|
|
||||||
(parsed (parse-track-title title))
|
|
||||||
(artist (getf parsed :artist))
|
|
||||||
(song (getf parsed :song))
|
|
||||||
(search-url (generate-music-search-url artist song)))
|
|
||||||
`(("title" . ,title)
|
|
||||||
("artist" . ,artist)
|
|
||||||
("song" . ,song)
|
|
||||||
("timestamp" . ,unix-timestamp)
|
|
||||||
("search_url" . ,search-url))))
|
|
||||||
tracks)))))))
|
|
||||||
|
|
||||||
;; API endpoint to get all tracks (for web player)
|
;; API endpoint to get all tracks (for web player)
|
||||||
(define-api asteroid/tracks () ()
|
(define-api asteroid/tracks () ()
|
||||||
"Get all tracks for web player"
|
"Get all tracks for web player"
|
||||||
|
|
|
||||||
|
|
@ -157,59 +157,3 @@
|
||||||
|
|
||||||
(api-output `(("status" . "success")
|
(api-output `(("status" . "success")
|
||||||
("message" . ,(format nil "Password reset for user: ~a" username)))))))
|
("message" . ,(format nil "Password reset for user: ~a" username)))))))
|
||||||
|
|
||||||
(define-api asteroid/user/activate (user-id active) ()
|
|
||||||
"API endpoint for setting the active state of an user account"
|
|
||||||
(format t "Activation of user: #~a set to ~a~%" user-id active)
|
|
||||||
(require-role :admin)
|
|
||||||
(with-error-handling
|
|
||||||
(let ((user (when user-id
|
|
||||||
(find-user-by-id user-id)))
|
|
||||||
(active (if (stringp active)
|
|
||||||
(parse-integer active)
|
|
||||||
active)))
|
|
||||||
|
|
||||||
(unless user
|
|
||||||
(error 'not-found-error :message "User not found"))
|
|
||||||
|
|
||||||
;; Change user active state
|
|
||||||
(let ((result (if (= 0 active)
|
|
||||||
(deactivate-user user-id)
|
|
||||||
(activate-user user-id))))
|
|
||||||
(if result
|
|
||||||
(api-output `(("status" . "success")
|
|
||||||
("message" . ,(format nil "User '~a' ~a."
|
|
||||||
(dm:field user "username")
|
|
||||||
(if (= 0 active)
|
|
||||||
"deactivated"
|
|
||||||
"activated")))))
|
|
||||||
(api-output `(("status" . "error")
|
|
||||||
("message" . ,(format nil "Could not ~a user '~a'."
|
|
||||||
(if (= 0 active)
|
|
||||||
"deactivated"
|
|
||||||
"activated")
|
|
||||||
(dm:field user "username"))))))))))
|
|
||||||
|
|
||||||
(define-api asteroid/user/role (user-id role) ()
|
|
||||||
"API endpoint for setting the access role of an user account"
|
|
||||||
(format t "Role of user: #~a set to ~a~%" user-id role)
|
|
||||||
(require-role :admin)
|
|
||||||
(with-error-handling
|
|
||||||
(let ((user (when user-id
|
|
||||||
(find-user-by-id user-id)))
|
|
||||||
(user-role (intern (string-upcase role) :keyword)))
|
|
||||||
|
|
||||||
(unless user
|
|
||||||
(error 'not-found-error :message "User not found"))
|
|
||||||
|
|
||||||
;; Change user role
|
|
||||||
(let ((result (update-user-role user-id user-role)))
|
|
||||||
(if result
|
|
||||||
(api-output `(("status" . "success")
|
|
||||||
("message" . ,(format nil "User '~a' is now a ~a."
|
|
||||||
(dm:field user "username")
|
|
||||||
role))))
|
|
||||||
(api-output `(("status" . "error")
|
|
||||||
("message" . ,(format nil "Could not set user '~a' as ~a."
|
|
||||||
(dm:field user "username")
|
|
||||||
role)))))))))
|
|
||||||
|
|
|
||||||
|
|
@ -91,21 +91,18 @@
|
||||||
;; Update user role
|
;; Update user role
|
||||||
(defun update-user-role (user-id new-role)
|
(defun update-user-role (user-id new-role)
|
||||||
(let ((form-data (ps:new (-form-data))))
|
(let ((form-data (ps:new (-form-data))))
|
||||||
(ps:chain form-data (append "user-id" user-id))
|
|
||||||
(ps:chain form-data (append "role" new-role))
|
(ps:chain form-data (append "role" new-role))
|
||||||
|
|
||||||
(ps:chain
|
(ps:chain
|
||||||
(fetch "/api/asteroid/user/role"
|
(fetch (+ "/api/asteroid/users/" user-id "/role")
|
||||||
(ps:create :method "POST" :body form-data))
|
(ps:create :method "POST" :body form-data))
|
||||||
(then (lambda (response) (ps:chain response (json))))
|
(then (lambda (response) (ps:chain response (json))))
|
||||||
(then (lambda (result)
|
(then (lambda (result)
|
||||||
;; Handle Radiance API data wrapping
|
(if (= (ps:@ result status) "success")
|
||||||
(let ((data (or (ps:@ result data) result)))
|
|
||||||
(if (= (ps:@ data status) "success")
|
|
||||||
(progn
|
(progn
|
||||||
(load-user-stats)
|
(load-user-stats)
|
||||||
(alert (ps:@ data message)))
|
(alert "User role updated successfully"))
|
||||||
(alert (+ "Error updating user role: " (ps:@ data message)))))))
|
(alert (+ "Error updating user role: " (ps:@ result message))))))
|
||||||
(catch (lambda (error)
|
(catch (lambda (error)
|
||||||
(ps:chain console (error "Error updating user role:" error))
|
(ps:chain console (error "Error updating user role:" error))
|
||||||
(alert "Error updating user role. Please try again."))))))
|
(alert "Error updating user role. Please try again."))))))
|
||||||
|
|
@ -115,52 +112,37 @@
|
||||||
(when (not (confirm "Are you sure you want to deactivate this user?"))
|
(when (not (confirm "Are you sure you want to deactivate this user?"))
|
||||||
(return))
|
(return))
|
||||||
|
|
||||||
(let ((form-data (ps:new (-form-data))))
|
|
||||||
(ps:chain form-data (append "user-id" user-id))
|
|
||||||
(ps:chain form-data (append "active" 0))
|
|
||||||
|
|
||||||
(ps:chain
|
(ps:chain
|
||||||
(fetch "/api/asteroid/user/activate"
|
(fetch (+ "/api/asteroid/users/" user-id "/deactivate")
|
||||||
(ps:create :method "POST" :body form-data))
|
(ps:create :method "POST"))
|
||||||
(then (lambda (response) (ps:chain response (json))))
|
(then (lambda (response) (ps:chain response (json))))
|
||||||
(then (lambda (result)
|
(then (lambda (result)
|
||||||
;; Handle Radiance API data wrapping
|
(if (= (ps:@ result status) "success")
|
||||||
(let ((data (or (ps:@ result data) result)))
|
|
||||||
(if (= (ps:@ data status) "success")
|
|
||||||
(progn
|
(progn
|
||||||
(load-users)
|
(load-users)
|
||||||
(load-user-stats)
|
(load-user-stats)
|
||||||
(alert (ps:@ data message)))
|
(alert "User deactivated successfully"))
|
||||||
(alert (+ "Error deactivating user: " (ps:@ data message)))))))
|
(alert (+ "Error deactivating user: " (ps:@ result message))))))
|
||||||
(catch (lambda (error)
|
(catch (lambda (error)
|
||||||
(ps:chain console (error "Error deactivating user:" error))
|
(ps:chain console (error "Error deactivating user:" error))
|
||||||
(alert "Error deactivating user. Please try again."))))))
|
(alert "Error deactivating user. Please try again.")))))
|
||||||
|
|
||||||
;; Activate user
|
;; Activate user
|
||||||
(defun activate-user (user-id)
|
(defun activate-user (user-id)
|
||||||
(when (not (confirm "Are you sure you want to activate this user?"))
|
|
||||||
(return))
|
|
||||||
|
|
||||||
(let ((form-data (ps:new (-form-data))))
|
|
||||||
(ps:chain form-data (append "user-id" user-id))
|
|
||||||
(ps:chain form-data (append "active" 1))
|
|
||||||
|
|
||||||
(ps:chain
|
(ps:chain
|
||||||
(fetch "/api/asteroid/user/activate"
|
(fetch (+ "/api/asteroid/users/" user-id "/activate")
|
||||||
(ps:create :method "POST" :body form-data))
|
(ps:create :method "POST"))
|
||||||
(then (lambda (response) (ps:chain response (json))))
|
(then (lambda (response) (ps:chain response (json))))
|
||||||
(then (lambda (result)
|
(then (lambda (result)
|
||||||
;; Handle Radiance API data wrapping
|
(if (= (ps:@ result status) "success")
|
||||||
(let ((data (or (ps:@ result data) result)))
|
|
||||||
(if (= (ps:@ data status) "success")
|
|
||||||
(progn
|
(progn
|
||||||
(load-users)
|
(load-users)
|
||||||
(load-user-stats)
|
(load-user-stats)
|
||||||
(alert (ps:@ data message)))
|
(alert "User activated successfully"))
|
||||||
(alert (+ "Error activating user: " (ps:@ data message)))))))
|
(alert (+ "Error activating user: " (ps:@ result message))))))
|
||||||
(catch (lambda (error)
|
(catch (lambda (error)
|
||||||
(ps:chain console (error "Error activating user:" error))
|
(ps:chain console (error "Error activating user:" error))
|
||||||
(alert "Error activating user. Please try again."))))))
|
(alert "Error activating user. Please try again.")))))
|
||||||
|
|
||||||
;; Toggle create user form
|
;; Toggle create user form
|
||||||
(defun toggle-create-user-form ()
|
(defun toggle-create-user-form ()
|
||||||
|
|
|
||||||
|
|
@ -143,131 +143,6 @@ body .now-playing{
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
body .recently-played-panel{
|
|
||||||
background: #1a2332;
|
|
||||||
border: 1px solid #2a3441;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel h3{
|
|
||||||
margin: 0 0 15px 0;
|
|
||||||
color: #00ff00;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list{
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list .loading,
|
|
||||||
body .recently-played-panel .recently-played-list .no-tracks,
|
|
||||||
body .recently-played-panel .recently-played-list .error{
|
|
||||||
text-align: center;
|
|
||||||
color: #888;
|
|
||||||
padding: 20px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list .error{
|
|
||||||
color: #ff4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-list{
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
max-height: none;
|
|
||||||
overflow-y: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-item{
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-bottom: 1px solid #2a3441;
|
|
||||||
-moz-transition: background-color 0.2s;
|
|
||||||
-o-transition: background-color 0.2s;
|
|
||||||
-webkit-transition: background-color 0.2s;
|
|
||||||
-ms-transition: background-color 0.2s;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-item LAST-CHILD{
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-item HOVER{
|
|
||||||
background-color: #2a3441;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-info{
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
grid-template-rows: auto auto;
|
|
||||||
gap: 2px 20px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-title{
|
|
||||||
color: #4488ff;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 1em;
|
|
||||||
grid-column: 1;
|
|
||||||
grid-row: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-artist{
|
|
||||||
color: #4488ff;
|
|
||||||
font-size: 0.9em;
|
|
||||||
grid-column: 1;
|
|
||||||
grid-row: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-time{
|
|
||||||
color: #888;
|
|
||||||
font-size: 0.85em;
|
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 1;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-panel .recently-played-list.track-meta{
|
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 2;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body .recently-played-list .allmusic-link{
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
color: #4488ff;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.85em;
|
|
||||||
letter-spacing: 0.08rem;
|
|
||||||
-moz-transition: all 0.2s;
|
|
||||||
-o-transition: all 0.2s;
|
|
||||||
-webkit-transition: all 0.2s;
|
|
||||||
-ms-transition: all 0.2s;
|
|
||||||
transition: all 0.2s;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-list .allmusic-link HOVER{
|
|
||||||
color: #00ff00;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .recently-played-list .allmusic-link svg{
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .back{
|
body .back{
|
||||||
color: #00ffff;
|
color: #00ffff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@
|
||||||
:grid-column 2
|
:grid-column 2
|
||||||
:grid-row 1
|
:grid-row 1
|
||||||
:text-align right))
|
:text-align right))
|
||||||
|
|
||||||
(.back
|
(.back
|
||||||
:color "#00ffff"
|
:color "#00ffff"
|
||||||
:text-decoration none
|
:text-decoration none
|
||||||
|
|
@ -306,7 +307,7 @@
|
||||||
:text-align center)
|
:text-align center)
|
||||||
|
|
||||||
((:and audio |::-webkit-media-controls-panel|)
|
((:and audio |::-webkit-media-controls-panel|)
|
||||||
:background-color "#1a2332")
|
:background-color "#1a1a1a")
|
||||||
|
|
||||||
;; ((:and audio (:or |::-webkit-media-controls-mute-button|
|
;; ((:and audio (:or |::-webkit-media-controls-mute-button|
|
||||||
;; |::-webkit-media-controls-play-button|
|
;; |::-webkit-media-controls-play-button|
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
// Recently Played Tracks functionality
|
|
||||||
|
|
||||||
async function updateRecentlyPlayed() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/asteroid/recently-played');
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
// Radiance wraps API responses in a data envelope
|
|
||||||
const data = result.data || result;
|
|
||||||
|
|
||||||
if (data.status === 'success' && data.tracks && data.tracks.length > 0) {
|
|
||||||
const listEl = document.getElementById('recently-played-list');
|
|
||||||
if (!listEl) return;
|
|
||||||
|
|
||||||
// Build HTML for tracks
|
|
||||||
let html = '<ul class="track-list">';
|
|
||||||
data.tracks.forEach((track, index) => {
|
|
||||||
const timeAgo = formatTimeAgo(track.timestamp);
|
|
||||||
html += `
|
|
||||||
<li class="track-item">
|
|
||||||
<div class="track-info">
|
|
||||||
<div class="track-title">${escapeHtml(track.song)}</div>
|
|
||||||
<div class="track-artist">${escapeHtml(track.artist)}</div>
|
|
||||||
<span class="track-time">${timeAgo}</span>
|
|
||||||
<div class="track-meta">
|
|
||||||
<a href="${track.search_url}" target="_blank" rel="noopener noreferrer" class="allmusic-link">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
|
||||||
<polyline points="15 3 21 3 21 9"></polyline>
|
|
||||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
|
||||||
</svg>
|
|
||||||
MusicBrainz
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
html += '</ul>';
|
|
||||||
listEl.innerHTML = html;
|
|
||||||
} else {
|
|
||||||
const listEl = document.getElementById('recently-played-list');
|
|
||||||
if (listEl) {
|
|
||||||
listEl.innerHTML = '<p class="no-tracks">No tracks played yet</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching recently played:', error);
|
|
||||||
const listEl = document.getElementById('recently-played-list');
|
|
||||||
if (listEl) {
|
|
||||||
listEl.innerHTML = '<p class="error">Error loading recently played tracks</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTimeAgo(timestamp) {
|
|
||||||
const now = Math.floor(Date.now() / 1000);
|
|
||||||
const diff = now - timestamp;
|
|
||||||
|
|
||||||
if (diff < 60) return 'Just now';
|
|
||||||
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
|
||||||
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
|
|
||||||
return `${Math.floor(diff / 86400)}d ago`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(text) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize on page load
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const panel = document.getElementById('recently-played-panel');
|
|
||||||
if (panel) {
|
|
||||||
updateRecentlyPlayed();
|
|
||||||
// Update every 30 seconds
|
|
||||||
setInterval(updateRecentlyPlayed, 30000);
|
|
||||||
} else {
|
|
||||||
const list = document.getElementById('recently-played-list');
|
|
||||||
if (list) {
|
|
||||||
updateRecentlyPlayed();
|
|
||||||
setInterval(updateRecentlyPlayed, 30000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -90,23 +90,20 @@ function hideUsersTable() {
|
||||||
async function updateUserRole(userId, newRole) {
|
async function updateUserRole(userId, newRole) {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('user-id', userId);
|
|
||||||
formData.append('role', newRole);
|
formData.append('role', newRole);
|
||||||
|
|
||||||
const response = await fetch('/api/asteroid/user/role', {
|
const response = await fetch(`/api/asteroid/users/${userId}/role`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
// Handle Radiance API data wrapping
|
|
||||||
const data = result.data || result;
|
|
||||||
|
|
||||||
if (data.status === 'success') {
|
if (result.status === 'success') {
|
||||||
loadUserStats();
|
loadUserStats();
|
||||||
alert(data.message);
|
alert('User role updated successfully');
|
||||||
} else {
|
} else {
|
||||||
alert('Error updating user role: ' + data.message);
|
alert('Error updating user role: ' + result.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating user role:', error);
|
console.error('Error updating user role:', error);
|
||||||
|
|
@ -120,25 +117,18 @@ async function deactivateUser(userId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const response = await fetch(`/api/asteroid/users/${userId}/deactivate`, {
|
||||||
formData.append('user-id', userId);
|
method: 'POST'
|
||||||
formData.append('active', 0);
|
|
||||||
|
|
||||||
const response = await fetch('/api/asteroid/user/activate', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
// Handle Radiance API data wrapping
|
|
||||||
const data = result.data || result;
|
|
||||||
|
|
||||||
if (data.status === 'success') {
|
if (result.status === 'success') {
|
||||||
loadUsers();
|
loadUsers();
|
||||||
loadUserStats();
|
loadUserStats();
|
||||||
alert(data.message);
|
alert('User deactivated successfully');
|
||||||
} else {
|
} else {
|
||||||
alert('Error deactivating user: ' + data.message);
|
alert('Error deactivating user: ' + result.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deactivating user:', error);
|
console.error('Error deactivating user:', error);
|
||||||
|
|
@ -147,31 +137,19 @@ async function deactivateUser(userId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function activateUser(userId) {
|
async function activateUser(userId) {
|
||||||
if (!confirm('Are you sure you want to activate this user?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const response = await fetch(`/api/asteroid/users/${userId}/activate`, {
|
||||||
const formData = new FormData();
|
method: 'POST'
|
||||||
formData.append('user-id', userId);
|
|
||||||
formData.append('active', 1);
|
|
||||||
|
|
||||||
const response = await fetch('/api/asteroid/user/activate', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
// Handle Radiance API data wrapping
|
|
||||||
const data = result.data || result;
|
|
||||||
|
|
||||||
if (data.status === 'success') {
|
if (result.status === 'success') {
|
||||||
loadUsers();
|
loadUsers();
|
||||||
loadUserStats();
|
loadUserStats();
|
||||||
alert(data.message);
|
alert('User activated successfully');
|
||||||
} else {
|
} else {
|
||||||
alert('Error activating user: ' + data.message);
|
alert('Error activating user: ' + result.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error activating user:', error);
|
console.error('Error activating user:', error);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<div id="recently-played-panel" class="recently-played-panel">
|
|
||||||
<h3>Recently Played</h3>
|
|
||||||
<div id="recently-played-list" class="recently-played-list">
|
|
||||||
<p class="loading">Loading...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -40,10 +40,7 @@
|
||||||
(defun find-user-by-id (user-id)
|
(defun find-user-by-id (user-id)
|
||||||
"Find a user by ID"
|
"Find a user by ID"
|
||||||
(format t "Looking for user with ID: ~a (type: ~a)~%" user-id (type-of user-id))
|
(format t "Looking for user with ID: ~a (type: ~a)~%" user-id (type-of user-id))
|
||||||
(let* ((user-id (if (stringp user-id)
|
(let ((user (dm:get-one "USERS" (db:query (:= '_id user-id)))))
|
||||||
(parse-integer user-id)
|
|
||||||
user-id))
|
|
||||||
(user (dm:get-one "USERS" (db:query (:= '_id user-id)))))
|
|
||||||
(when user
|
(when user
|
||||||
(format t "Found user '~a' with id #~a~%"
|
(format t "Found user '~a' with id #~a~%"
|
||||||
(dm:field user "username")
|
(dm:field user "username")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue