From 1cbd7e9d1799d8b64afcfc3a620f9bc0d30403f3 Mon Sep 17 00:00:00 2001 From: enzo Date: Sun, 22 Mar 2026 01:39:37 +0100 Subject: [PATCH] fix: GetSessions scan robuste (sql.NullString) + formatDate adaptatif MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scan des colonnes datetime via sql.NullString au lieu de time.Time pour éviter les échecs silencieux dus aux formats mixtes SQLite (CURRENT_TIMESTAMP vs RFC3339) - Log explicite si un scan échoue (au lieu du continue silencieux) - formatDate frontend adapte le format SQLite "YYYY-MM-DD HH:MM:SS" en ISO pour new Date() Co-Authored-By: Claude Sonnet 4.6 --- backend/internal/api/auth.go | 24 ++++++++++++++---------- frontend/js/app.js | 9 ++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/backend/internal/api/auth.go b/backend/internal/api/auth.go index 9e0348b..f6ad350 100644 --- a/backend/internal/api/auth.go +++ b/backend/internal/api/auth.go @@ -359,23 +359,27 @@ func (h *AuthHandler) GetSessions(w http.ResponseWriter, r *http.Request) { defer rows.Close() type Session struct { - ID int64 `json:"id"` - UserAgent string `json:"user_agent"` - IP string `json:"ip"` - CreatedAt time.Time `json:"created_at"` - LastUsedAt *time.Time `json:"last_used_at"` - ExpiresAt time.Time `json:"expires_at"` + ID int64 `json:"id"` + UserAgent string `json:"user_agent"` + IP string `json:"ip"` + CreatedAt string `json:"created_at"` + LastUsedAt *string `json:"last_used_at"` + ExpiresAt string `json:"expires_at"` } sessions := []Session{} for rows.Next() { var s Session - var lastUsed sql.NullTime - if err := rows.Scan(&s.ID, &s.UserAgent, &s.IP, &s.CreatedAt, &lastUsed, &s.ExpiresAt); err != nil { + var createdAt, expiresAt sql.NullString + var lastUsedAt sql.NullString + if err := rows.Scan(&s.ID, &s.UserAgent, &s.IP, &createdAt, &lastUsedAt, &expiresAt); err != nil { + log.Printf("[GetSessions] scan error userID=%d: %v", claims.UserID, err) continue } - if lastUsed.Valid { - s.LastUsedAt = &lastUsed.Time + s.CreatedAt = createdAt.String + s.ExpiresAt = expiresAt.String + if lastUsedAt.Valid && lastUsedAt.String != "" { + s.LastUsedAt = &lastUsedAt.String } sessions = append(sessions, s) } diff --git a/frontend/js/app.js b/frontend/js/app.js index 82a47c3..dd903e7 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -686,9 +686,12 @@ document.addEventListener('alpine:init', () => { this.revoking = { ...this.revoking, [id]: false } }, - formatDate(iso) { - if (!iso) return '—' - const d = new Date(iso) + formatDate(raw) { + if (!raw) return '—' + // SQLite retourne "YYYY-MM-DD HH:MM:SS" ou RFC3339 selon le driver + // new Date() gère les deux si on normalise le séparateur + const d = new Date(raw.includes('T') ? raw : raw.replace(' ', 'T') + 'Z') + if (isNaN(d.getTime())) return raw return d.toLocaleString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) },