feat: implement chat persistence with MongoDB
- Add full chat persistence to database (conversations and messages) - Implement MongoChatRepository with cursor-based pagination - Add getConversations/getConversation endpoints in ChatController - Save user and assistant messages in ChatService.processMessage() - Track respondedAction (confirm/reject) on proposed event messages - Load existing messages on chat screen mount - Add addMessages() bulk action and chatMessageToMessageData() helper to ChatStore - Add RespondedAction type and UpdateMessageDTO to shared types
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
CreateEventDTO,
|
||||
UpdateEventDTO,
|
||||
EventAction,
|
||||
CreateMessageDTO,
|
||||
} from "@caldav/shared";
|
||||
import { ChatRepository, EventRepository, AIProvider } from "./interfaces";
|
||||
import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters";
|
||||
@@ -252,6 +253,18 @@ export class ChatService {
|
||||
userId: string,
|
||||
data: SendMessageDTO,
|
||||
): Promise<ChatResponse> {
|
||||
let conversationId = data.conversationId;
|
||||
if (!conversationId) {
|
||||
const conversation = await this.chatRepo.createConversation(userId);
|
||||
conversationId = conversation.id;
|
||||
}
|
||||
|
||||
// Save user message
|
||||
await this.chatRepo.createMessage(conversationId, {
|
||||
sender: "user",
|
||||
content: data.content,
|
||||
});
|
||||
|
||||
const response = await getTestResponse(
|
||||
responseIndex,
|
||||
this.eventRepo,
|
||||
@@ -259,15 +272,14 @@ export class ChatService {
|
||||
);
|
||||
responseIndex++;
|
||||
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId: data.conversationId || "temp-conv-id",
|
||||
// Save and then return assistant response
|
||||
const answerMessage = await this.chatRepo.createMessage(conversationId, {
|
||||
sender: "assistant",
|
||||
content: response.content,
|
||||
proposedChange: response.proposedChange,
|
||||
};
|
||||
});
|
||||
|
||||
return { message, conversationId: message.conversationId };
|
||||
return { message: answerMessage, conversationId: conversationId };
|
||||
}
|
||||
|
||||
async confirmEvent(
|
||||
@@ -279,6 +291,10 @@ export class ChatService {
|
||||
eventId?: string,
|
||||
updates?: UpdateEventDTO,
|
||||
): Promise<ChatResponse> {
|
||||
// Update original message with respondedAction
|
||||
await this.chatRepo.updateMessage(messageId, { respondedAction: "confirm" });
|
||||
|
||||
// Perform the actual event operation
|
||||
let content: string;
|
||||
|
||||
if (action === "create" && event) {
|
||||
@@ -298,12 +314,12 @@ export class ChatService {
|
||||
content = "Ungültige Aktion.";
|
||||
}
|
||||
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId,
|
||||
// Save response message to DB
|
||||
const message = await this.chatRepo.createMessage(conversationId, {
|
||||
sender: "assistant",
|
||||
content,
|
||||
};
|
||||
});
|
||||
|
||||
return { message, conversationId };
|
||||
}
|
||||
|
||||
@@ -312,17 +328,34 @@ export class ChatService {
|
||||
conversationId: string,
|
||||
messageId: string,
|
||||
): Promise<ChatResponse> {
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId,
|
||||
// Update original message with respondedAction
|
||||
await this.chatRepo.updateMessage(messageId, { respondedAction: "reject" });
|
||||
|
||||
// Save response message to DB
|
||||
const message = await this.chatRepo.createMessage(conversationId, {
|
||||
sender: "assistant",
|
||||
content: "Der Vorschlag wurde abgelehnt.",
|
||||
};
|
||||
});
|
||||
|
||||
return { message, conversationId };
|
||||
}
|
||||
|
||||
async getConversations(userId: string): Promise<ConversationSummary[]> {
|
||||
throw new Error("Not implemented");
|
||||
const conversations = await this.chatRepo.getConversationsByUser(userId);
|
||||
|
||||
// For each conversation, get the last message
|
||||
const summaries: ConversationSummary[] = await Promise.all(
|
||||
conversations.map(async (conv) => {
|
||||
const messages = await this.chatRepo.getMessages(conv.id, { limit: 1 });
|
||||
return {
|
||||
id: conv.id,
|
||||
lastMessage: messages[0],
|
||||
createdAt: conv.createdAt,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return summaries;
|
||||
}
|
||||
|
||||
async getConversation(
|
||||
@@ -330,6 +363,14 @@ export class ChatService {
|
||||
conversationId: string,
|
||||
options?: GetMessagesOptions,
|
||||
): Promise<ChatMessage[]> {
|
||||
throw new Error("Not implemented");
|
||||
// Verify conversation belongs to user
|
||||
const conversations = await this.chatRepo.getConversationsByUser(userId);
|
||||
const conversation = conversations.find((c) => c.id === conversationId);
|
||||
|
||||
if (!conversation) {
|
||||
throw new Error("Conversation not found");
|
||||
}
|
||||
|
||||
return this.chatRepo.getMessages(conversationId, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Conversation,
|
||||
CreateMessageDTO,
|
||||
GetMessagesOptions,
|
||||
UpdateMessageDTO,
|
||||
} from "@caldav/shared";
|
||||
|
||||
export interface ChatRepository {
|
||||
@@ -15,8 +16,14 @@ export interface ChatRepository {
|
||||
conversationId: string,
|
||||
options?: GetMessagesOptions,
|
||||
): Promise<ChatMessage[]>;
|
||||
|
||||
createMessage(
|
||||
conversationId: string,
|
||||
message: CreateMessageDTO,
|
||||
): Promise<ChatMessage>;
|
||||
|
||||
updateMessage(
|
||||
messageId: string,
|
||||
updates: UpdateMessageDTO,
|
||||
): Promise<ChatMessage | null>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user