diff --git a/.env.example b/.env.example index 504b8c4..d9c445a 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.7.2 +JAMA_VERSION=0.7.4 # Default admin credentials (used on FIRST RUN only) ADMIN_NAME=Admin User diff --git a/backend/package.json b/backend/package.json index 2c9c3e3..9377125 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.7.2", + "version": "0.7.4", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/index.js b/backend/src/index.js index b3ea298..6c76c65 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -21,7 +21,14 @@ const PORT = process.env.PORT || 3000; // Init DB initDb(); seedAdmin(); -getOrCreateSupportGroup(); // Ensure Support group exists +// Ensure Support group exists and all admins are members +const supportGroupId = getOrCreateSupportGroup(); +if (supportGroupId) { + const db = getDb(); + const admins = db.prepare("SELECT id FROM users WHERE role = 'admin' AND status = 'active'").all(); + const insert = db.prepare('INSERT OR IGNORE INTO group_members (group_id, user_id) VALUES (?, ?)'); + for (const a of admins) insert.run(supportGroupId, a.id); +} // Middleware app.use(cors()); diff --git a/backend/src/routes/about.js b/backend/src/routes/about.js index 6fa2d24..3f77aeb 100644 --- a/backend/src/routes/about.js +++ b/backend/src/routes/about.js @@ -29,6 +29,9 @@ router.get('/', (req, res) => { ...DEFAULTS, ...overrides, version: process.env.JAMA_VERSION || process.env.TEAMCHAT_VERSION || 'dev', + // Always expose original app identity — not overrideable via about.json or settings + default_app_name: 'jama', + default_logo: '/icons/jama.png', }; // Never expose docker_image — removed from UI diff --git a/backend/src/routes/users.js b/backend/src/routes/users.js index 79b67ec..791a481 100644 --- a/backend/src/routes/users.js +++ b/backend/src/routes/users.js @@ -3,7 +3,7 @@ const bcrypt = require('bcryptjs'); const multer = require('multer'); const path = require('path'); const router = express.Router(); -const { getDb, addUserToPublicGroups } = require('../models/db'); +const { getDb, addUserToPublicGroups, getOrCreateSupportGroup } = require('../models/db'); const { authMiddleware, adminMiddleware } = require('../middleware/auth'); const avatarStorage = multer.diskStorage({ @@ -121,6 +121,13 @@ router.post('/', authMiddleware, adminMiddleware, (req, res) => { `).run(resolvedName, email, hash, role === 'admin' ? 'admin' : 'member'); addUserToPublicGroups(result.lastInsertRowid); + // Admin users are automatically added to the Support group + if (role === 'admin') { + const supportGroupId = getOrCreateSupportGroup(); + if (supportGroupId) { + db.prepare('INSERT OR IGNORE INTO group_members (group_id, user_id) VALUES (?, ?)').run(supportGroupId, result.lastInsertRowid); + } + } const user = db.prepare('SELECT id, name, email, role, status, must_change_password, created_at FROM users WHERE id = ?').get(result.lastInsertRowid); res.json({ user }); }); @@ -151,8 +158,15 @@ router.post('/bulk', authMiddleware, adminMiddleware, (req, res) => { const resolvedName = resolveUniqueName(db, name); const pw = (u.password || '').trim() || defaultPw; const hash = bcrypt.hashSync(pw, 10); - const r = insertUser.run(resolvedName, email, hash, u.role === 'admin' ? 'admin' : 'member'); + const newRole = u.role === 'admin' ? 'admin' : 'member'; + const r = insertUser.run(resolvedName, email, hash, newRole); addUserToPublicGroups(r.lastInsertRowid); + if (newRole === 'admin') { + const supportGroupId = getOrCreateSupportGroup(); + if (supportGroupId) { + db.prepare('INSERT OR IGNORE INTO group_members (group_id, user_id) VALUES (?, ?)').run(supportGroupId, r.lastInsertRowid); + } + } results.created.push(email); } catch (e) { results.skipped.push({ email, reason: e.message }); @@ -184,6 +198,13 @@ router.patch('/:id/role', authMiddleware, adminMiddleware, (req, res) => { if (target.is_default_admin) return res.status(403).json({ error: 'Cannot modify default admin role' }); if (!['member', 'admin'].includes(role)) return res.status(400).json({ error: 'Invalid role' }); db.prepare("UPDATE users SET role = ?, updated_at = datetime('now') WHERE id = ?").run(role, target.id); + // If promoted to admin, ensure they're in the Support group + if (role === 'admin') { + const supportGroupId = getOrCreateSupportGroup(); + if (supportGroupId) { + db.prepare('INSERT OR IGNORE INTO group_members (group_id, user_id) VALUES (?, ?)').run(supportGroupId, target.id); + } + } res.json({ success: true }); }); diff --git a/build.sh b/build.sh index 5262cf1..c015d3f 100644 --- a/build.sh +++ b/build.sh @@ -11,9 +11,9 @@ # REGISTRY=ghcr.io/yourname ./build.sh 1.2.0 push # REGISTRY=yourdockerhubuser ./build.sh 1.2.0 push # ───────────────────────────────────────────────────────────── -set -euo pipefail +set -euo pipefail -VERSION="${1:-0.7.2}" +VERSION="${1:-0.7.4}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index 3c79d38..a733e83 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.7.2", + "version": "0.7.4", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/AboutModal.jsx b/frontend/src/components/AboutModal.jsx index 426af5e..4657151 100644 --- a/frontend/src/components/AboutModal.jsx +++ b/frontend/src/components/AboutModal.jsx @@ -22,20 +22,19 @@ function BuiltWithValue({ value }) { } export default function AboutModal({ onClose }) { - const [settings, setSettings] = useState({ app_name: 'jama', app_version: '' }); const [about, setAbout] = useState(null); useEffect(() => { - api.getSettings().then(({ settings }) => setSettings(settings)).catch(() => {}); fetch('/api/about') .then(r => r.json()) .then(({ about }) => setAbout(about)) .catch(() => {}); }, []); - const appName = settings.app_name || 'jama'; - // Version always mirrors Settings window — from settings API (env var) - const version = settings.app_version || about?.version || ''; + // Always use the original app identity — not the user-customised settings name/logo + const appName = about?.default_app_name || 'jama'; + const logoSrc = about?.default_logo || '/icons/jama.png'; + const version = about?.version || ''; const a = about || {}; const rows = [ @@ -55,7 +54,7 @@ export default function AboutModal({ onClose }) {
just another messaging app