COMMIT 16MAR
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Message from '../models/Message.js';
|
||||
import OrganizationMember from '../models/OrganizationMember.js';
|
||||
import { query } from '../config/database.js';
|
||||
|
||||
// Send a message
|
||||
export const sendMessage = async (req: Request, res: Response) => {
|
||||
@@ -21,36 +20,27 @@ export const sendMessage = async (req: Request, res: Response) => {
|
||||
return res.status(400).json({ error: 'Destinatário e mensagem são obrigatórios.' });
|
||||
}
|
||||
|
||||
if (message.length > 255) {
|
||||
return res.status(400).json({ error: 'Mensagem muito longa (máximo 255 caracteres).' });
|
||||
}
|
||||
|
||||
// Check if there's already a pending (unread) message from this user to that user
|
||||
const existingMessage = await Message.findOne({
|
||||
organizationId,
|
||||
fromUserId,
|
||||
toUserId,
|
||||
isRead: false,
|
||||
});
|
||||
const existingRes = await query(
|
||||
'SELECT * FROM gpi.messages WHERE organization_id = $1 AND from_user_id = $2 AND to_user_id = $3 AND is_read = false',
|
||||
[organizationId, fromUserId, toUserId]
|
||||
);
|
||||
|
||||
if (existingMessage) {
|
||||
// Update existing message instead of creating a new one
|
||||
existingMessage.message = message;
|
||||
existingMessage.updatedAt = new Date();
|
||||
await existingMessage.save();
|
||||
return res.json(existingMessage);
|
||||
if (existingRes.rows.length > 0) {
|
||||
const updatedRes = await query(
|
||||
'UPDATE gpi.messages SET message = $1, updated_at = NOW() WHERE id = $2 RETURNING *',
|
||||
[message, existingRes.rows[0].id]
|
||||
);
|
||||
return res.json(updatedRes.rows[0]);
|
||||
}
|
||||
|
||||
// Create new message
|
||||
const newMessage = new Message({
|
||||
organizationId,
|
||||
fromUserId,
|
||||
toUserId,
|
||||
message,
|
||||
});
|
||||
const insertRes = await query(
|
||||
`INSERT INTO gpi.messages (organization_id, from_user_id, to_user_id, message)
|
||||
VALUES ($1, $2, $3, $4) RETURNING *`,
|
||||
[organizationId, fromUserId, toUserId, message]
|
||||
);
|
||||
|
||||
await newMessage.save();
|
||||
res.status(201).json(newMessage);
|
||||
res.status(201).json(insertRes.rows[0]);
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
res.status(500).json({ error: 'Erro ao enviar mensagem.' });
|
||||
@@ -63,34 +53,25 @@ export const getUnreadMessages = async (req: Request, res: Response) => {
|
||||
const toUserId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
if (!organizationId) {
|
||||
return res.status(400).json({ error: 'Organização não selecionada.' });
|
||||
if (!organizationId || !toUserId) {
|
||||
return res.status(400).json({ error: 'Contexto incompleto.' });
|
||||
}
|
||||
|
||||
if (!toUserId) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
||||
}
|
||||
const sql = `
|
||||
SELECT m.*, u.name as "fromUserName", u.email as "fromUserEmail"
|
||||
FROM gpi.messages m
|
||||
LEFT JOIN gpi.users u ON m.from_user_id = u.clerk_id OR m.from_user_id = u.logto_id
|
||||
WHERE m.organization_id = $1 AND m.to_user_id = $2 AND m.is_read = false AND m.is_archived = false AND m.is_deleted_by_recipient = false
|
||||
ORDER BY m.created_at DESC
|
||||
`;
|
||||
const result = await query(sql, [organizationId, toUserId]);
|
||||
|
||||
const messages = result.rows.map(m => ({
|
||||
...m,
|
||||
fromUser: { name: m.fromUserName, email: m.fromUserEmail }
|
||||
}));
|
||||
|
||||
const messages = await Message.find({
|
||||
organizationId,
|
||||
toUserId,
|
||||
isRead: false,
|
||||
isArchived: false,
|
||||
isDeletedByRecipient: false,
|
||||
}).sort({ createdAt: -1 });
|
||||
|
||||
// Populate sender info
|
||||
const messagesWithSender = await Promise.all(
|
||||
messages.map(async (msg) => {
|
||||
const sender = await OrganizationMember.findOne({ userId: msg.fromUserId });
|
||||
return {
|
||||
...msg.toObject(),
|
||||
fromUser: sender ? { name: sender.name, email: sender.email } : null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
res.json(messagesWithSender);
|
||||
res.json(messages);
|
||||
} catch (error) {
|
||||
console.error('Error getting unread messages:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar mensagens.' });
|
||||
@@ -104,29 +85,16 @@ export const markMessageAsRead = async (req: Request, res: Response) => {
|
||||
const userId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
if (!organizationId) {
|
||||
return res.status(400).json({ error: 'Organização não selecionada.' });
|
||||
}
|
||||
const result = await query(
|
||||
'UPDATE gpi.messages SET is_read = true, read_at = NOW() WHERE id = $1 AND organization_id = $2 AND to_user_id = $3 RETURNING *',
|
||||
[id, organizationId, userId]
|
||||
);
|
||||
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
||||
}
|
||||
|
||||
const message = await Message.findOne({
|
||||
_id: id,
|
||||
organizationId,
|
||||
toUserId: userId,
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
||||
}
|
||||
|
||||
message.isRead = true;
|
||||
message.readAt = new Date();
|
||||
await message.save();
|
||||
|
||||
res.json(message);
|
||||
res.json(result.rows[0]);
|
||||
} catch (error) {
|
||||
console.error('Error marking message as read:', error);
|
||||
res.status(500).json({ error: 'Erro ao marcar mensagem como lida.' });
|
||||
@@ -139,32 +107,25 @@ export const getMyPendingMessages = async (req: Request, res: Response) => {
|
||||
const fromUserId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
if (!organizationId) {
|
||||
return res.status(400).json({ error: 'Organização não selecionada.' });
|
||||
if (!organizationId || !fromUserId) {
|
||||
return res.status(400).json({ error: 'Contexto incompleto.' });
|
||||
}
|
||||
|
||||
if (!fromUserId) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
||||
}
|
||||
const sql = `
|
||||
SELECT m.*, u.name as "toUserName", u.email as "toUserEmail"
|
||||
FROM gpi.messages m
|
||||
LEFT JOIN gpi.users u ON m.to_user_id = u.clerk_id OR m.to_user_id = u.logto_id
|
||||
WHERE m.organization_id = $1 AND m.from_user_id = $2 AND m.is_read = false
|
||||
ORDER BY m.created_at DESC
|
||||
`;
|
||||
const result = await query(sql, [organizationId, fromUserId]);
|
||||
|
||||
const messages = result.rows.map(m => ({
|
||||
...m,
|
||||
toUser: { name: m.toUserName, email: m.toUserEmail }
|
||||
}));
|
||||
|
||||
const messages = await Message.find({
|
||||
organizationId,
|
||||
fromUserId,
|
||||
isRead: false,
|
||||
}).sort({ createdAt: -1 });
|
||||
|
||||
// Populate recipient info
|
||||
const messagesWithRecipient = await Promise.all(
|
||||
messages.map(async (msg) => {
|
||||
const recipient = await OrganizationMember.findOne({ userId: msg.toUserId });
|
||||
return {
|
||||
...msg.toObject(),
|
||||
toUser: recipient ? { name: recipient.name, email: recipient.email } : null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
res.json(messagesWithRecipient);
|
||||
res.json(messages);
|
||||
} catch (error) {
|
||||
console.error('Error getting pending messages:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar mensagens pendentes.' });
|
||||
@@ -178,26 +139,15 @@ export const deleteMessage = async (req: Request, res: Response) => {
|
||||
const userId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
if (!organizationId) {
|
||||
return res.status(400).json({ error: 'Organização não selecionada.' });
|
||||
const result = await query(
|
||||
'DELETE FROM gpi.messages WHERE id = $1 AND from_user_id = $2 AND organization_id = $3 AND is_read = false',
|
||||
[id, userId, organizationId]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
return res.status(404).json({ error: 'Mensagem não encontrada ou já lida.' });
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
||||
}
|
||||
|
||||
const message = await Message.findOne({
|
||||
_id: id,
|
||||
organizationId,
|
||||
fromUserId: userId,
|
||||
isRead: false, // Can only delete unread messages
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
return res.status(404).json({ error: 'Mensagem não encontrada ou já foi lida.' });
|
||||
}
|
||||
|
||||
await message.deleteOne();
|
||||
res.json({ message: 'Mensagem deletada com sucesso.' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting message:', error);
|
||||
@@ -205,20 +155,21 @@ export const deleteMessage = async (req: Request, res: Response) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Recipient deletes/archives a message
|
||||
// Recipient archives a message
|
||||
export const archiveMessage = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
||||
if (!message) return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
||||
const result = await query(
|
||||
'UPDATE gpi.messages SET is_archived = true, is_read = true WHERE id = $1 AND to_user_id = $2 AND organization_id = $3 RETURNING *',
|
||||
[id, userId, organizationId]
|
||||
);
|
||||
|
||||
message.isArchived = true;
|
||||
message.isRead = true; // Arquivar implica ler
|
||||
await message.save();
|
||||
res.json(message);
|
||||
if (result.rows.length === 0) return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (error) {
|
||||
console.error('Error archiving message:', error);
|
||||
res.status(500).json({ error: 'Erro ao arquivar mensagem.' });
|
||||
@@ -231,11 +182,13 @@ export const recipientDeleteMessage = async (req: Request, res: Response) => {
|
||||
const userId = req.appUser?.externalId;
|
||||
const organizationId = req.headers['x-organization-id'] as string;
|
||||
|
||||
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
||||
if (!message) return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
||||
const result = await query(
|
||||
'UPDATE gpi.messages SET is_deleted_by_recipient = true WHERE id = $1 AND to_user_id = $2 AND organization_id = $3 RETURNING *',
|
||||
[id, userId, organizationId]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
||||
|
||||
message.isDeletedByRecipient = true;
|
||||
await message.save();
|
||||
res.json({ message: 'Mensagem excluída com sucesso.' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting message:', error);
|
||||
|
||||
Reference in New Issue
Block a user