diff --git a/.env.example b/.env.example index b26a250..aeaa94a 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ PROJECT_NAME=jama # Image version to run (set by build.sh, or use 'latest') -JAMA_VERSION=0.9.30 +JAMA_VERSION=0.9.31 # App port — the host port Docker maps to the container PORT=3000 diff --git a/backend/package.json b/backend/package.json index b6a5c8f..8e3fe6b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.9.30", + "version": "0.9.31", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/routes/usergroups.js b/backend/src/routes/usergroups.js index c0c5eef..4655c71 100644 --- a/backend/src/routes/usergroups.js +++ b/backend/src/routes/usergroups.js @@ -69,6 +69,15 @@ router.post('/multigroup', authMiddleware, adminMiddleware, (req, res) => { if (db.prepare('SELECT id FROM multi_group_dms WHERE LOWER(name) = LOWER(?)').get(name.trim())) { return res.status(400).json({ error: 'Name already in use' }); } + // Check for duplicate user group set + const newGroupIds = [...new Set(userGroupIds.map(Number).filter(Boolean))].sort(); + const allDms = db.prepare('SELECT id, name FROM multi_group_dms').all(); + for (const existing of allDms) { + const existingIds = db.prepare('SELECT user_group_id FROM multi_group_dm_members WHERE multi_group_dm_id = ?').all(existing.id).map(r => r.user_group_id).sort(); + if (existingIds.length === newGroupIds.length && existingIds.every((id, i) => id === newGroupIds[i])) { + return res.status(400).json({ error: `DM not created — "${existing.name}" already exists with the same member groups.` }); + } + } const admin = db.prepare('SELECT id FROM users WHERE is_default_admin = 1').get(); const dmResult = db.prepare(`INSERT INTO groups (name, type, owner_id, is_managed) VALUES (?, 'private', ?, 1)`).run(name.trim(), admin?.id || req.user.id); const dmGroupId = dmResult.lastInsertRowid; @@ -179,6 +188,17 @@ router.post('/', authMiddleware, adminMiddleware, (req, res) => { if (db.prepare('SELECT id FROM user_groups WHERE LOWER(name) = LOWER(?)').get(name.trim())) { return res.status(400).json({ error: 'A group with that name already exists' }); } + // Check for duplicate member set + const newIds = [...new Set((Array.isArray(memberIds) ? memberIds : []).map(Number).filter(Boolean))].sort(); + if (newIds.length > 0) { + const allGroups = db.prepare('SELECT id, name FROM user_groups').all(); + for (const existing of allGroups) { + const existingIds = db.prepare('SELECT user_id FROM user_group_members WHERE user_group_id = ?').all(existing.id).map(r => r.user_id).sort(); + if (existingIds.length === newIds.length && existingIds.every((id, i) => id === newIds[i])) { + return res.status(400).json({ error: `Group not created — "${existing.name}" already exists with the same members.` }); + } + } + } const admin = db.prepare('SELECT id FROM users WHERE is_default_admin = 1').get(); const dmResult = db.prepare(`INSERT INTO groups (name, type, owner_id, is_readonly, is_direct, is_managed) VALUES (?, 'private', ?, 0, 0, 1)`).run(name.trim(), admin?.id || req.user.id); const dmGroupId = dmResult.lastInsertRowid; diff --git a/build.sh b/build.sh index 07e8c61..4c6ac88 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.9.30}" +VERSION="${1:-0.9.31}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index 1c25a8a..0d98fcc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.9.30", + "version": "0.9.31", "private": true, "scripts": { "dev": "vite",