v0.11.9 fixed tenant isolation bug
This commit is contained in:
@@ -3,6 +3,8 @@ const bcrypt = require('bcryptjs');
|
||||
const { query, queryOne, queryResult, exec, getOrCreateSupportGroup } = require('../models/db');
|
||||
const { generateToken, authMiddleware, setActiveSession, clearActiveSession } = require('../middleware/auth');
|
||||
|
||||
const R = (schema, type, id) => `${schema}:${type}:${id}`;
|
||||
|
||||
module.exports = function(io) {
|
||||
const router = express.Router();
|
||||
|
||||
@@ -25,7 +27,7 @@ module.exports = function(io) {
|
||||
const token = generateToken(user.id);
|
||||
const ua = req.headers['user-agent'] || '';
|
||||
const device = await setActiveSession(req.schema, user.id, token, ua);
|
||||
if (io) io.to(`user:${user.id}`).emit('session:displaced', { device });
|
||||
if (io) io.to(R(req.schema,'user',user.id)).emit('session:displaced', { device });
|
||||
|
||||
const { password: _, ...userSafe } = user;
|
||||
res.json({ token, user: userSafe, mustChangePassword: !!user.must_change_password, rememberMe: !!rememberMe });
|
||||
@@ -87,10 +89,10 @@ module.exports = function(io) {
|
||||
SELECT m.*, u.name AS user_name, u.display_name AS user_display_name, u.avatar AS user_avatar
|
||||
FROM messages m JOIN users u ON m.user_id = u.id WHERE m.id = $1
|
||||
`, [mr.rows[0].id]);
|
||||
if (newMsg) { newMsg.reactions = []; io.to(`group:${groupId}`).emit('message:new', newMsg); }
|
||||
if (newMsg) { newMsg.reactions = []; io.to(R(req.schema,'group',groupId)).emit('message:new', newMsg); }
|
||||
|
||||
const admins = await query(req.schema, "SELECT id FROM users WHERE role = 'admin' AND status = 'active'");
|
||||
for (const a of admins) io.to(`user:${a.id}`).emit('notification:new', { type: 'support', groupId });
|
||||
for (const a of admins) io.to(R(req.schema,'user',a.id)).emit('notification:new', { type: 'support', groupId });
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
|
||||
@@ -10,16 +10,19 @@ function deleteImageFile(imageUrl) {
|
||||
catch (e) { console.warn('[Groups] Could not delete image:', e.message); }
|
||||
}
|
||||
|
||||
// Schema-aware room name helper
|
||||
const R = (schema, type, id) => `${schema}:${type}:${id}`;
|
||||
|
||||
module.exports = (io) => {
|
||||
|
||||
async function emitGroupNew(schema, io, groupId) {
|
||||
const group = await queryOne(schema, 'SELECT * FROM groups WHERE id=$1', [groupId]);
|
||||
if (!group) return;
|
||||
if (group.type === 'public') {
|
||||
io.emit('group:new', { group });
|
||||
io.to(R(schema, 'schema', 'all')).emit('group:new', { group });
|
||||
} else {
|
||||
const members = await query(schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [groupId]);
|
||||
for (const m of members) io.to(`user:${m.user_id}`).emit('group:new', { group });
|
||||
for (const m of members) io.to(R(schema, 'user', m.user_id)).emit('group:new', { group });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +35,7 @@ async function emitGroupUpdated(schema, io, groupId) {
|
||||
} else {
|
||||
uids = await query(schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [groupId]);
|
||||
}
|
||||
for (const m of uids) io.to(`user:${m.user_id}`).emit('group:updated', { group });
|
||||
for (const m of uids) io.to(R(schema, 'user', m.user_id)).emit('group:updated', { group });
|
||||
}
|
||||
|
||||
// GET all groups for current user
|
||||
@@ -240,9 +243,9 @@ router.post('/:id/members', authMiddleware, async (req, res) => {
|
||||
[mr.rows[0].id]
|
||||
);
|
||||
sysMsg.reactions = [];
|
||||
io.to(`group:${group.id}`).emit('message:new', sysMsg);
|
||||
io.in(`user:${userId}`).socketsJoin(`group:${group.id}`);
|
||||
io.to(`user:${userId}`).emit('group:new', { group });
|
||||
io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
|
||||
io.in(R(req.schema,'user',userId)).socketsJoin(R(req.schema,'group',group.id));
|
||||
io.to(R(req.schema,'user',userId)).emit('group:new', { group });
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
@@ -276,9 +279,9 @@ router.delete('/:id/members/:userId', authMiddleware, async (req, res) => {
|
||||
[mr.rows[0].id]
|
||||
);
|
||||
sysMsg.reactions = [];
|
||||
io.to(`group:${group.id}`).emit('message:new', sysMsg);
|
||||
io.in(`user:${targetId}`).socketsLeave(`group:${group.id}`);
|
||||
io.to(`user:${targetId}`).emit('group:deleted', { groupId: group.id });
|
||||
io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
|
||||
io.in(R(req.schema,'user',targetId)).socketsLeave(R(req.schema,'group',group.id));
|
||||
io.to(R(req.schema,'user',targetId)).emit('group:deleted', { groupId: group.id });
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
@@ -302,9 +305,9 @@ router.delete('/:id/leave', authMiddleware, async (req, res) => {
|
||||
[mr.rows[0].id]
|
||||
);
|
||||
sysMsg.reactions = [];
|
||||
io.to(`group:${group.id}`).emit('message:new', sysMsg);
|
||||
io.in(`user:${userId}`).socketsLeave(`group:${group.id}`);
|
||||
io.to(`user:${userId}`).emit('group:deleted', { groupId: group.id });
|
||||
io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
|
||||
io.in(R(req.schema,'user',userId)).socketsLeave(R(req.schema,'group',group.id));
|
||||
io.to(R(req.schema,'user',userId)).emit('group:deleted', { groupId: group.id });
|
||||
if (group.is_direct) {
|
||||
const remaining = await queryOne(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1 LIMIT 1', [group.id]);
|
||||
if (remaining) await exec(req.schema, 'UPDATE groups SET owner_id=$1, updated_at=NOW() WHERE id=$2', [remaining.user_id, group.id]);
|
||||
@@ -340,7 +343,7 @@ router.delete('/:id', authMiddleware, async (req, res) => {
|
||||
const imageMessages = await query(req.schema, 'SELECT image_url FROM messages WHERE group_id=$1 AND image_url IS NOT NULL', [group.id]);
|
||||
await exec(req.schema, 'DELETE FROM groups WHERE id=$1', [group.id]);
|
||||
for (const msg of imageMessages) deleteImageFile(msg.image_url);
|
||||
for (const uid of members) io.to(`user:${uid}`).emit('group:deleted', { groupId: group.id });
|
||||
for (const uid of members) io.to(R(req.schema,'user',uid)).emit('group:deleted', { groupId: group.id });
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ function deleteImageFile(imageUrl) {
|
||||
catch (e) { console.warn('[Messages] Could not delete image:', e.message); }
|
||||
}
|
||||
|
||||
const R = (schema, type, id) => `${schema}:${type}:${id}`;
|
||||
|
||||
module.exports = function(io) {
|
||||
const router = express.Router();
|
||||
const { authMiddleware } = require('../middleware/auth');
|
||||
@@ -98,7 +100,7 @@ module.exports = function(io) {
|
||||
WHERE m.id=$1
|
||||
`, [r.rows[0].id]);
|
||||
message.reactions = [];
|
||||
io.to(`group:${req.params.groupId}`).emit('message:new', message);
|
||||
io.to(R(req.schema,'group',req.params.groupId)).emit('message:new', message);
|
||||
res.json({ message });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
@@ -121,7 +123,7 @@ module.exports = function(io) {
|
||||
[r.rows[0].id]
|
||||
);
|
||||
message.reactions = [];
|
||||
io.to(`group:${req.params.groupId}`).emit('message:new', message);
|
||||
io.to(R(req.schema,'group',req.params.groupId)).emit('message:new', message);
|
||||
res.json({ message });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
@@ -140,7 +142,7 @@ module.exports = function(io) {
|
||||
const imageUrl = message.image_url;
|
||||
await exec(req.schema, 'UPDATE messages SET is_deleted=TRUE, content=NULL, image_url=NULL WHERE id=$1', [message.id]);
|
||||
deleteImageFile(imageUrl);
|
||||
io.to(`group:${message.group_id}`).emit('message:deleted', { messageId: message.id, groupId: message.group_id });
|
||||
io.to(R(req.schema,'group',message.group_id)).emit('message:deleted', { messageId: message.id, groupId: message.group_id });
|
||||
res.json({ success: true, messageId: message.id });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
@@ -164,7 +166,7 @@ module.exports = function(io) {
|
||||
'SELECT r.emoji, r.user_id, u.name AS user_name FROM reactions r JOIN users u ON r.user_id=u.id WHERE r.message_id=$1',
|
||||
[message.id]
|
||||
);
|
||||
io.to(`group:${message.group_id}`).emit('reaction:updated', { messageId: message.id, reactions });
|
||||
io.to(R(req.schema,'group',message.group_id)).emit('reaction:updated', { messageId: message.id, reactions });
|
||||
res.json({ reactions });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ const router = express.Router();
|
||||
const { query, queryOne, queryResult, exec } = require('../models/db');
|
||||
const { authMiddleware, adminMiddleware, teamManagerMiddleware } = require('../middleware/auth');
|
||||
|
||||
const R = (schema, type, id) => `${schema}:${type}:${id}`;
|
||||
|
||||
module.exports = function(io) {
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
@@ -18,14 +20,14 @@ async function postSysMsg(schema, groupId, actorId, content) {
|
||||
u.hide_admin_tag AS user_hide_admin_tag, u.about_me AS user_about_me, u.allow_dm AS user_allow_dm
|
||||
FROM messages m JOIN users u ON m.user_id=u.id WHERE m.id=$1
|
||||
`, [r.rows[0].id]);
|
||||
if (msg) { msg.reactions = []; io.to(`group:${groupId}`).emit('message:new', msg); }
|
||||
if (msg) { msg.reactions = []; io.to(R(schema,'group',groupId)).emit('message:new', msg); }
|
||||
}
|
||||
|
||||
async function addUserSilent(schema, dmGroupId, userId) {
|
||||
await exec(schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [dmGroupId, userId]);
|
||||
io.in(`user:${userId}`).socketsJoin(`group:${dmGroupId}`);
|
||||
io.in(R(schema,'user',userId)).socketsJoin(R(schema,'group',dmGroupId));
|
||||
const dmGroup = await queryOne(schema, 'SELECT * FROM groups WHERE id=$1', [dmGroupId]);
|
||||
if (dmGroup) io.to(`user:${userId}`).emit('group:new', { group: dmGroup });
|
||||
if (dmGroup) io.to(R(schema,'user',userId)).emit('group:new', { group: dmGroup });
|
||||
}
|
||||
|
||||
async function addUser(schema, dmGroupId, userId, actorId) {
|
||||
@@ -36,8 +38,8 @@ async function addUser(schema, dmGroupId, userId, actorId) {
|
||||
|
||||
async function removeUser(schema, dmGroupId, userId, actorId) {
|
||||
await exec(schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [dmGroupId, userId]);
|
||||
io.in(`user:${userId}`).socketsLeave(`group:${dmGroupId}`);
|
||||
io.to(`user:${userId}`).emit('group:deleted', { groupId: dmGroupId });
|
||||
io.in(R(schema,'user',userId)).socketsLeave(R(schema,'group',dmGroupId));
|
||||
io.to(R(schema,'user',userId)).emit('group:deleted', { groupId: dmGroupId });
|
||||
const u = await queryOne(schema, 'SELECT name,display_name FROM users WHERE id=$1', [userId]);
|
||||
await postSysMsg(schema, dmGroupId, actorId, `${u?.display_name||u?.name||'A user'} has been removed from the conversation.`);
|
||||
}
|
||||
@@ -154,8 +156,8 @@ router.patch('/multigroup/:id', authMiddleware, teamManagerMiddleware, async (re
|
||||
`, [mg.id, uid]);
|
||||
if (!stillIn) {
|
||||
await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [mg.dm_group_id, uid]);
|
||||
io.in(`user:${uid}`).socketsLeave(`group:${mg.dm_group_id}`);
|
||||
io.to(`user:${uid}`).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
io.in(R(schema,'user',uid)).socketsLeave(R(schema,'group',mg.dm_group_id));
|
||||
io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
}
|
||||
}
|
||||
await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `A group has been removed from this conversation.`);
|
||||
@@ -173,7 +175,7 @@ router.delete('/multigroup/:id', authMiddleware, teamManagerMiddleware, async (r
|
||||
if (mg.dm_group_id) {
|
||||
const members = (await query(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [mg.dm_group_id])).map(r => r.user_id);
|
||||
await exec(req.schema, 'DELETE FROM groups WHERE id=$1', [mg.dm_group_id]);
|
||||
for (const uid of members) io.to(`user:${uid}`).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
for (const uid of members) io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
}
|
||||
await exec(req.schema, 'DELETE FROM multi_group_dms WHERE id=$1', [mg.id]);
|
||||
res.json({ success: true });
|
||||
@@ -281,8 +283,8 @@ router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) =>
|
||||
`, [mg.id, uid]);
|
||||
if (!stillIn) {
|
||||
await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [mg.dm_group_id, uid]);
|
||||
io.in(`user:${uid}`).socketsLeave(`group:${mg.dm_group_id}`);
|
||||
io.to(`user:${uid}`).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
io.in(R(schema,'user',uid)).socketsLeave(R(schema,'group',mg.dm_group_id));
|
||||
io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: mg.dm_group_id });
|
||||
}
|
||||
}
|
||||
if (addedUids.length > 0) await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `Members were added to group "${ug.name}" and have joined this conversation.`);
|
||||
@@ -303,7 +305,7 @@ router.delete('/:id', authMiddleware, teamManagerMiddleware, async (req, res) =>
|
||||
if (ug.dm_group_id) {
|
||||
const members = (await query(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [ug.dm_group_id])).map(r => r.user_id);
|
||||
await exec(req.schema, 'DELETE FROM groups WHERE id=$1', [ug.dm_group_id]);
|
||||
for (const uid of members) { io.in(`user:${uid}`).socketsLeave(`group:${ug.dm_group_id}`); io.to(`user:${uid}`).emit('group:deleted', { groupId: ug.dm_group_id }); }
|
||||
for (const uid of members) { io.in(R(schema,'user',uid)).socketsLeave(R(schema,'group',ug.dm_group_id)); io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: ug.dm_group_id }); }
|
||||
}
|
||||
await exec(req.schema, 'DELETE FROM user_groups WHERE id=$1', [ug.id]);
|
||||
res.json({ success: true });
|
||||
|
||||
Reference in New Issue
Block a user