- 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 <noreply@anthropic.com>
- auth.go: upsertUser utilise toujours SELECT explicite au lieu de LastInsertId()
qui retournait un rowid obsolète pour ON CONFLICT DO UPDATE sur ligne existante
- auth.go: vérifier l'erreur de l'INSERT refresh_tokens (était silencieusement ignorée)
- auth.go: logs détaillés dans Refresh handler pour diagnostiquer les 401
- db.go: repairSchema() ajoute les colonnes manquantes (ip, last_used_at) dans les
bases où migration 002 était partiellement appliquée (ancien bug multi-statements)
- app.js: tryRefresh et fetchMe affichent le vrai message d'erreur du backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- auth store fetchMe(): sync theme/sidebar_position/lang from DB to localStorage+stores on login/refresh
- profilePage setters: PATCH /api/auth/preferences on every preference change
- updatesPage: add history tab (GET /api/updates/history) with job list, click to view output
- dashboardPage: load shortcuts from settings API, fall back to defaults if none configured
- settingsPage: new Raccourcis tab to add/remove/configure dashboard shortcuts (saved as JSON)
- settings.go: expose dashboard_shortcuts in publicSettings for GET/PUT access
- pages.css: add .history-table, .shortcut-row styles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fetchMe: handle ALL non-ok responses (not just 401) by calling tryRefresh
→ avoids user=null when backend returns 404/500/any error
- DOMContentLoaded guard: check isAuthenticated instead of localStorage token
→ immediate redirect if fetchMe+tryRefresh both fail, no more flash of dashboard
- Cookie Secure flag: check X-Forwarded-Proto header for Traefik/proxy setup
→ cookie gets Secure=true when behind TLS-terminating reverse proxy
- db.go migrate(): split SQL by ; and exec each statement separately
→ fixes SQLite multi-statement limitation (only first stmt was executed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: migration 002 adds user_agent/ip/last_used_at to refresh_tokens
- Backend: GET /api/auth/sessions + DELETE /api/auth/sessions/{id} endpoints
- Frontend: profile page — sessions section (browser, IP, datetime, revoke)
- Frontend: web manifest + SVG icon for PWA support
- Frontend: remove language selector from all navbars (moved to profile page)
- Frontend: neu-btn--icon-sm class for square icon-only buttons (theme/logout/edit)
- Frontend: manifest link added to all 9 HTML pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Icônes :
- Sidebar navItems : couleur distincte par section (iconStyle via data-binding)
- Sidebar footer user : couleur primary
- Navbar : logout → danger, soleil → amber, lune → blue
- Panel widgets : œil visible → success, caché → muted
Boutons :
- `.neu-btn--sm:has(> i:only-child)` → carré 2rem×2rem automatiquement
(theme, logout, mode édition) sans modifier le HTML
Sidebar :
- --sidebar-width-collapsed : 64px → 52px
- Sidebar réduite : icônes centrées (justify-content center)
Langue :
- Setter vide sur `lang` dans navbar() pour corriger x-model avec getter
(le @change gère la vraie mise à jour du store)
Service Worker :
- Enregistrement depuis /ws.sw.js (scope /) au lieu de /js/ws.sw.js (scope /js/)
- build.mjs : copie ws.sw.js vers dist/ root en plus de dist/js/
LXC arrêté :
- checkTarget() : skip si target.status !== 'running' → évite les 502 SSH
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Resize widget via drag sur le coin bas-droit (mousedown → mousemove →
snap à taille 1 ou 2 colonnes selon distance)
- DnD live : les autres widgets se déplacent pendant le drag (onDragEnter
réordonne le tableau + onDragEnd restaure si annulé)
- Mode édition : panel latéral avec tous les widgets (œil toggle visible/masqué),
survol d'une entrée met en avant (outline) le widget correspondant dans la grille
- Bouton mode édition : icône seule (lnid-pencil-1)
- Correction noms d'icônes LineIcons (lnid-pencil-1, lnid-eye, lnid-eye-closed,
lnid-cross → supprimé en faveur de toggles dans le panel)
- Suppression des classes CSS obsolètes (edit-mode-banner, widget-add-btn, etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le CSS vendor avait un bug de sélecteur : [class^="lnid-"] sans pseudo-élément
recevait position:absolute + width/height:100%, faisant que les icônes
recouvraient tout l'écran. Correction : sélecteurs ::before et ::after explicites.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Intégration LineIcons Duotone (css/ + toutes les pages)
- Remplacement de tous les symboles Unicode par des icônes lnid-*
- Page profile.html : préférences thème, position sidebar, langue
- Dashboard : système de widgets add/remove + drag-and-drop natif
- Sidebar gauche/droite configurable per-user (data-sidebar CSS + FOUC script)
- Store ui : sidebarPosition, applySidebarPosition(), setSidebarPosition()
- Composant profilePage() dans app.js
- nav.profile ajouté dans fr.json et en.json
- SUIVI.md mis à jour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extraction de tous les styles inline en css/pages.css (chargé globalement)
pour corriger le CSS cassé lors des navigations Swup
- Correction types WebSocket : proxmox_resources → resources_update
et msg.data → msg.payload (format réel du hub Go)
- Ajout d'un fetch HTTP immédiat dans dashboardPage/proxmoxPage
pour éviter l'attente du premier tick (10s) du polling WS
- Correction msg.payload pour les updates (update_output/done/error)
- Ajout class terminal-wrapper sur .main-layout de terminal.html
pour le fullscreen height sans affecter les autres pages
- Création SUIVI.md : état d'implémentation vs instruction.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le backend retourne { access_token: "...", user: {...} } pas { token: "..." }.
Le store Alpine lisait data.token → undefined → stockait "undefined" en localStorage
→ toutes les requêtes API échouaient avec 401.
Corrigé dans login() et tryRefresh().
Ajout d'un guard synchrone immédiat (pas de token → redirect login sans attendre fetchMe).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tous les appels pct passent par sudo -n pour les sessions SSH non-root.
GetPackages est résilient : utilise l'output même si exit code != 0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pct n'est pas dans le PATH SSH par défaut (exit 127).
Corrigé dans GetPackages, GetTargets et executeUpdate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remplace pct list SSH (permissions aléatoires) par /api/proxmox/lxc
- Lance checkAll() automatiquement à l'arrivée sur la page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: GET /api/updates/targets (pct list via SSH)
- Backend: GET /api/updates/packages?target= (apt list --upgradable)
- Frontend: grille de cards par cible (host + chaque LXC)
- Bouton Check/Update par card, liste paquets dépliable (version actuelle → nouvelle)
- Boutons globaux "Tout vérifier" et "Tout mettre à jour"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Install.vue et Settings.vue : remplace le champ unique "PVEAPIToken=..."
par deux inputs distincts — Token ID (ex: enzo@pam!panel) et Secret
(uuid). L'assemblage PVEAPIToken=ID=Secret se fait côté frontend avant
envoi. Plus besoin de connaître le format interne.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Session F5 :
- auth.store: restoreSession() essaie fetchMe() avec le token existant
(< 15 min → fonctionne sans cookie), puis tryRefresh() en fallback
- router: appelle restoreSession() au premier chargement au lieu de tryRefresh()
Paramètres infrastructure :
- Champs ssh_password et proxmox_token en write-only (vide = pas de changement)
- SettingsHandler: accepte les clés chiffrées, chiffre avant stockage
- Permet de corriger le token Proxmox invalide sans réinstallation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- router: tryRefresh() au premier chargement → plus besoin de se
reconnecter après F5 (user restauré depuis le cookie refresh)
- migration 003: terminal marqué is_core=1 + is_enabled=1
- proxmox.go: logs pour diagnostiquer l'erreur 502 (visible dans
Paramètres → Logs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sidebar : retrait des liens files, logs, services (non implémentés)
- Migration 001 : suppression des inserts files/logs/services
- Migration 002 : DELETE des modules inexistants en DB existante
- logbuffer : ring buffer mémoire branché sur log.SetOutput
- GET /api/settings/logs : retourne les 300 dernières lignes de log
- Settings : onglet Logs avec auto-refresh (5s/10s/30s/60s/désactivé, défaut 10s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug principal : l'SSHAuthenticator est créé au démarrage avec host=""
(DB vide avant installation). Après configure, il gardait host vide.
Le login lisait maintenant le ssh_host depuis la DB à chaque requête.
Logs ajoutés :
- ssh_auth.go : dial SSH, succès, échec avec détail d'erreur
- auth.go : host SSH utilisé, résultat auth à chaque login
- updates.go : credentials SSH, démarrage/fin de job
- terminal.go : ouverture/échec session SSH
Frontend :
- auth.store.ts : gère les réponses non-JSON sur erreur HTTP
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.go : detectPublicURL utilise https pour tout domaine public
même si Traefik envoie X-Forwarded-Proto: http en interne
- fr.json / en.json : échappe le @ dans proxmoxTokenHint avec {'@'}
(vue-i18n interprétait @realm comme un linked message → SyntaxError)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- helpers.go : corrige le cas r.Body == nil (panic → erreur explicite)
- install.go : ajout logs étape par étape pour TestSSH (TCP, auth SSH)
sans jamais logger le mot de passe (longueur uniquement)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nginx -t échoue au build car le hostname 'backend' n'existe
pas encore — il est résolu au runtime par le DNS Docker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>