diff --git a/.env.example b/.env.example index 199d65e..94aff1b 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ TZ=UTC # Copy this file to .env and customize # Image version to run (set by build.sh, or use 'latest') -JAMA_VERSION=0.6.4 +JAMA_VERSION=0.6.5 # Default admin credentials (used on FIRST RUN only) ADMIN_NAME=Admin User diff --git a/backend/package.json b/backend/package.json index 9fc9b9c..62bbed9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.6.4", + "version": "0.6.5", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/models/db.js b/backend/src/models/db.js index 63684cd..18bcf25 100644 --- a/backend/src/models/db.js +++ b/backend/src/models/db.js @@ -182,6 +182,12 @@ function initDb() { console.log('[DB] Migration: added direct_peer2_id column'); } catch (e) { /* column already exists */ } + // Migration: last_login timestamp per user + try { + db.exec("ALTER TABLE users ADD COLUMN last_login TEXT"); + console.log('[DB] Migration: added last_login column'); + } catch (e) { /* column already exists */ } + // Migration: help_dismissed preference per user try { db.exec("ALTER TABLE users ADD COLUMN help_dismissed INTEGER NOT NULL DEFAULT 0"); diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index 8a1ace5..8901747 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -24,6 +24,9 @@ router.post('/login', (req, res) => { const valid = bcrypt.compareSync(password, user.password); if (!valid) return res.status(401).json({ error: 'Invalid credentials' }); + // Record last login timestamp + db.prepare("UPDATE users SET last_login = datetime('now') WHERE id = ?").run(user.id); + const token = generateToken(user.id); const ua = req.headers['user-agent'] || ''; const device = setActiveSession(user.id, token, ua); // displaces prior session on same device class diff --git a/backend/src/routes/users.js b/backend/src/routes/users.js index 876d15e..9fc4b64 100644 --- a/backend/src/routes/users.js +++ b/backend/src/routes/users.js @@ -49,7 +49,7 @@ function getDefaultPassword(db) { router.get('/', authMiddleware, adminMiddleware, (req, res) => { const db = getDb(); const users = db.prepare(` - SELECT id, name, email, role, status, is_default_admin, must_change_password, avatar, about_me, display_name, created_at + SELECT id, name, email, role, status, is_default_admin, must_change_password, avatar, about_me, display_name, created_at, last_login FROM users WHERE status != 'deleted' ORDER BY created_at ASC `).all(); diff --git a/build.sh b/build.sh index 14aed0d..870a09c 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.6.4}" +VERSION="${1:-0.6.5}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index abd7ba2..fa8384d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.6.4", + "version": "0.6.5", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/HelpModal.jsx b/frontend/src/components/HelpModal.jsx index 8c96093..c031f7d 100644 --- a/frontend/src/components/HelpModal.jsx +++ b/frontend/src/components/HelpModal.jsx @@ -22,7 +22,6 @@ export default function HelpModal({ onClose, dismissed: initialDismissed }) { setDismissed(val); try { await api.dismissHelp(val); - if (val) onClose(); // immediately close when "do not show again" checked } catch (_) {} }; diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx index 9559759..dba58fa 100644 --- a/frontend/src/components/Sidebar.jsx +++ b/frontend/src/components/Sidebar.jsx @@ -32,7 +32,9 @@ function useAppSettings() { useEffect(() => { const name = settings.app_name || 'jama'; - document.title = name; + // Preserve any unread badge prefix already set by Chat.jsx + const prefix = document.title.match(/^(\(\d+\)\s*)/)?.[1] || ''; + document.title = prefix + name; const logoUrl = settings.logo_url; const faviconUrl = logoUrl || '/icons/jama.png'; let link = document.querySelector("link[rel~='icon']"); diff --git a/frontend/src/components/UserManagerModal.jsx b/frontend/src/components/UserManagerModal.jsx index 386d8f4..e6f1555 100644 --- a/frontend/src/components/UserManagerModal.jsx +++ b/frontend/src/components/UserManagerModal.jsx @@ -91,7 +91,12 @@ function UserRow({ u, onUpdated }) { {u.status !== 'active' && {u.status}} {!!u.is_default_admin && Default Admin} -