245 lines
8.1 KiB
TypeScript
245 lines
8.1 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import Message from '../models/Message.js';
|
|
import OrganizationMember from '../models/OrganizationMember.js';
|
|
|
|
// Send a message
|
|
export const sendMessage = async (req: Request, res: Response) => {
|
|
try {
|
|
const { toUserId, message } = req.body;
|
|
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 (!fromUserId) {
|
|
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
|
}
|
|
|
|
if (!toUserId || !message) {
|
|
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,
|
|
});
|
|
|
|
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);
|
|
}
|
|
|
|
// Create new message
|
|
const newMessage = new Message({
|
|
organizationId,
|
|
fromUserId,
|
|
toUserId,
|
|
message,
|
|
});
|
|
|
|
await newMessage.save();
|
|
res.status(201).json(newMessage);
|
|
} catch (error) {
|
|
console.error('Error sending message:', error);
|
|
res.status(500).json({ error: 'Erro ao enviar mensagem.' });
|
|
}
|
|
};
|
|
|
|
// Get unread messages for current user
|
|
export const getUnreadMessages = async (req: Request, res: Response) => {
|
|
try {
|
|
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 (!toUserId) {
|
|
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
|
}
|
|
|
|
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);
|
|
} catch (error) {
|
|
console.error('Error getting unread messages:', error);
|
|
res.status(500).json({ error: 'Erro ao buscar mensagens.' });
|
|
}
|
|
};
|
|
|
|
// Mark message as read
|
|
export const markMessageAsRead = async (req: Request, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
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.' });
|
|
}
|
|
|
|
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) {
|
|
return res.status(404).json({ error: 'Mensagem não encontrada.' });
|
|
}
|
|
|
|
message.isRead = true;
|
|
message.readAt = new Date();
|
|
await message.save();
|
|
|
|
res.json(message);
|
|
} catch (error) {
|
|
console.error('Error marking message as read:', error);
|
|
res.status(500).json({ error: 'Erro ao marcar mensagem como lida.' });
|
|
}
|
|
};
|
|
|
|
// Get my pending (unread) sent messages
|
|
export const getMyPendingMessages = async (req: Request, res: Response) => {
|
|
try {
|
|
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 (!fromUserId) {
|
|
return res.status(401).json({ error: 'Usuário não autenticado.' });
|
|
}
|
|
|
|
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);
|
|
} catch (error) {
|
|
console.error('Error getting pending messages:', error);
|
|
res.status(500).json({ error: 'Erro ao buscar mensagens pendentes.' });
|
|
}
|
|
};
|
|
|
|
// Delete a message (only if unread and sender is the current user)
|
|
export const deleteMessage = async (req: Request, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
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.' });
|
|
}
|
|
|
|
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);
|
|
res.status(500).json({ error: 'Erro ao deletar mensagem.' });
|
|
}
|
|
};
|
|
|
|
// Recipient deletes/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.' });
|
|
|
|
message.isArchived = true;
|
|
message.isRead = true; // Arquivar implica ler
|
|
await message.save();
|
|
res.json(message);
|
|
} catch (error) {
|
|
console.error('Error archiving message:', error);
|
|
res.status(500).json({ error: 'Erro ao arquivar mensagem.' });
|
|
}
|
|
};
|
|
|
|
export const recipientDeleteMessage = 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.' });
|
|
|
|
message.isDeletedByRecipient = true;
|
|
await message.save();
|
|
res.json({ message: 'Mensagem excluída com sucesso.' });
|
|
} catch (error) {
|
|
console.error('Error deleting message:', error);
|
|
res.status(500).json({ error: 'Erro ao excluir mensagem.' });
|
|
}
|
|
};
|