This commit is contained in:
2026-03-09 14:36:19 -04:00
parent f37fe0086f
commit 42ad779750
40 changed files with 1928 additions and 593 deletions

View File

@@ -32,9 +32,10 @@ app.use('/uploads', express.static('/app/uploads'));
// API Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/groups', require('./routes/groups'));
app.use('/api/groups', require('./routes/groups')(io));
app.use('/api/messages', require('./routes/messages'));
app.use('/api/settings', require('./routes/settings'));
app.use('/api/about', require('./routes/about'));
app.use('/api/push', pushRouter);
// Link preview proxy
@@ -128,14 +129,27 @@ io.on('connection', (socket) => {
// Broadcast online status
io.emit('user:online', { userId });
// Join personal room for direct notifications
socket.join(`user:${userId}`);
// Join rooms for all user's groups
const db = getDb();
const publicGroups = db.prepare("SELECT id FROM groups WHERE type = 'public'").all();
for (const g of publicGroups) socket.join(`group:${g.id}`);
const privateGroups = db.prepare("SELECT group_id FROM group_members WHERE user_id = ?").all(userId);
for (const g of privateGroups) socket.join(`group:${g.group_id}`);
// When a new group is created and pushed to this socket, join its room
socket.on('group:join-room', ({ groupId }) => {
socket.join(`group:${groupId}`);
});
// When a user leaves a group, remove them from the socket room
socket.on('group:leave-room', ({ groupId }) => {
socket.leave(`group:${groupId}`);
});
// Handle new message
socket.on('message:send', async (data) => {
const { groupId, content, replyToId, imageUrl, linkPreview } = data;
@@ -271,12 +285,31 @@ io.on('connection', (socket) => {
socket.on('message:delete', (data) => {
const { messageId } = data;
const db = getDb();
const message = db.prepare('SELECT m.*, g.type as group_type, g.owner_id as group_owner_id FROM messages m JOIN groups g ON m.group_id = g.id WHERE m.id = ?').get(messageId);
const message = db.prepare(`
SELECT m.*, g.type as group_type, g.owner_id as group_owner_id, g.is_direct
FROM messages m JOIN groups g ON m.group_id = g.id WHERE m.id = ?
`).get(messageId);
if (!message) return;
const canDelete = message.user_id === userId ||
(socket.user.role === 'admin' && message.group_type === 'public') ||
(message.group_type === 'private' && message.group_owner_id === userId);
const isAdmin = socket.user.role === 'admin';
const isOwner = message.group_owner_id === userId;
const isAuthor = message.user_id === userId;
// Rules:
// 1. Author can always delete their own message
// 2. Admin can delete in any public group or any group they're a member of
// 3. Group owner can delete any message in their group
// 4. In direct messages: author + owner rules apply (no blanket block)
let canDelete = isAuthor || isOwner;
if (!canDelete && isAdmin) {
if (message.group_type === 'public') {
canDelete = true;
} else {
// Admin can delete in private/direct groups they're a member of
const membership = db.prepare('SELECT id FROM group_members WHERE group_id = ? AND user_id = ?').get(message.group_id, userId);
if (membership) canDelete = true;
}
}
if (!canDelete) return;