import { Conversation } from "../models/Conversation.model.js"; import { Message } from "../models/Message.model.js"; export async function listConversations(userId, { limit = 50, offset = 0 } = {}) { const safeLimit = Math.min(Number(limit) || 50, 100); const safeOffset = Math.max(Number(offset) || 0, 0); return Conversation.query() .where({ UsuarioId: userId }) .orderBy("UpdatedAt", "desc") .limit(safeLimit) .offset(safeOffset) .select("Id", "Title", "CreatedAt", "UpdatedAt") .select(Conversation.relatedQuery("messages").count().as("MessageCount")); } export async function createConversation(userId, title = "Nova conversa") { const conv = await Conversation.query().insert({ UsuarioId: userId, Title: String(title).slice(0, 255) }); return { id: conv.Id, title }; } export async function getConversationMessages(conversationId, userId) { const conv = await Conversation.query().findOne({ Id: conversationId, UsuarioId: userId }); if (!conv) return null; const msgs = await Message.query() .where({ ConversationId: conversationId }) .orderBy("SentAt", "asc") .select("Id", "Role", "Content", "Sources", "SentAt"); return msgs; } export async function verifyConversationOwner(conversationId, userId) { const conv = await Conversation.query().findOne({ Id: conversationId, UsuarioId: userId }); return Boolean(conv); } export async function getRecentMessages(conversationId, userId, limit = 12) { if (userId) { const owns = await verifyConversationOwner(conversationId, userId); if (!owns) return []; } const msgs = await Message.query() .where({ ConversationId: conversationId }) .orderBy("SentAt", "desc") .limit(limit) .select("Role", "Content"); return msgs.reverse(); } export async function addMessage(conversationId, { role, content, sources = null }) { await Conversation.transaction(async (trx) => { await Message.query(trx).insert({ ConversationId: conversationId, Role: role, Content: content, Sources: sources ? JSON.stringify(sources) : null }); await Conversation.query(trx) .patch({ UpdatedAt: trx.fn.now() }) .where({ Id: conversationId }); }); } export async function updateConversationTitle(conversationId, userId, title) { await Conversation.query() .patch({ Title: String(title).slice(0, 255), UpdatedAt: Conversation.knex().fn.now() }) .where({ Id: conversationId, UsuarioId: userId }); } export async function deleteConversation(conversationId, userId) { await Conversation.query().delete().where({ Id: conversationId, UsuarioId: userId }); } export async function getConversationForExport(conversationId, userId) { const conv = await Conversation.query().findOne({ Id: conversationId, UsuarioId: userId }); if (!conv) return null; const msgs = await Message.query() .where({ ConversationId: conversationId }) .orderBy("SentAt", "asc") .select("Role", "Content", "SentAt"); return { title: conv.Title, createdAt: conv.CreatedAt, messages: msgs }; } export async function exportConversationAsMarkdown(conversationId, userId) { const data = await getConversationForExport(conversationId, userId); if (!data) return null; const date = new Date(data.createdAt).toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" }); const lines = [`# ${data.title}`, `*Exportado em ${date}*`, ""]; for (const msg of data.messages) { const author = msg.Role === "user" ? "**Você**" : "**Oráculo**"; lines.push(`${author}:`, "", msg.Content, ""); lines.push("---", ""); } return lines.join("\n"); }