diff --git a/auth-routes.lisp b/auth-routes.lisp index 406915f..0734465 100644 --- a/auth-routes.lisp +++ b/auth-routes.lisp @@ -53,6 +53,14 @@ (setf (session:field "user-id") nil) (radiance:redirect "/")) +;; Helper to convert Common Lisp universal time to Unix epoch +(defun cl-time-to-unix (cl-time) + "Convert Common Lisp universal time to Unix epoch. + CL epoch is 1900-01-01, Unix epoch is 1970-01-01. + Difference is 2208988800 seconds." + (when cl-time + (- cl-time 2208988800))) + ;; API: Get all users (admin only) (define-api asteroid/users () () "API endpoint to get all users" @@ -66,8 +74,8 @@ ("email" . ,(dm:field user "email")) ("role" . ,(dm:field user "role")) ("active" . ,(= (dm:field user "active") 1)) - ("created-date" . ,(dm:field user "created-date")) - ("last-login" . ,(dm:field user "last-login")))) + ("created-date" . ,(cl-time-to-unix (dm:field user "created-date"))) + ("last-login" . ,(cl-time-to-unix (dm:field user "last-login"))))) users))))) (error (e) (api-output `(("status" . "error") diff --git a/parenscript/users.lisp b/parenscript/users.lisp index a2c5971..ffeca08 100644 --- a/parenscript/users.lisp +++ b/parenscript/users.lisp @@ -57,9 +57,13 @@ "" "" "" (if (ps:@ user active) "✅ Active" "❌ Inactive") "" - "" (if (ps:getprop user "last-login") - (ps:chain (ps:new (-date (ps:getprop user "last-login"))) (to-locale-string)) - "Never") "" + "" (let ((login-val (ps:getprop user "last-login"))) + (if login-val + (let ((date-val (if (> login-val 9999999999) + login-val ; Already milliseconds + (* login-val 1000)))) ; Convert seconds to ms + (ps:chain (ps:new (-date date-val)) (to-locale-string))) + "Never")) "" "" (if (ps:@ user active) (+ "") diff --git a/user-management.lisp b/user-management.lisp index 4f23df6..d579a0a 100644 --- a/user-management.lisp +++ b/user-management.lisp @@ -70,13 +70,14 @@ (when (and (= 1 user-active) (verify-password password user-password)) ;; Update last login using data-model (database agnostic) - ;; Use ISO 8601 format that PostgreSQL TIMESTAMP can parse + ;; Use ISO 8601 format in UTC that PostgreSQL TIMESTAMP can parse (handler-case (progn (setf (dm:field user "last-login") (local-time:format-timestring nil (local-time:now) :format '(:year "-" (:month 2) "-" (:day 2) " " - (:hour 2) ":" (:min 2) ":" (:sec 2)))) + (:hour 2) ":" (:min 2) ":" (:sec 2)) + :timezone local-time:+utc-zone+)) (dm:save user)) (error (e) (format t "Warning: Could not update last-login: ~a~%" e)))