feat: persist AI chat history to server-side JSON file
Chat history was stored only in sessionStorage, causing member ID and DOB to be lost when the session expired or corrupted — requiring logout/login. Now history is saved to a per-user JSON file on the backend and loaded on mount, so the LLM always has full conversation context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { storage } from "../storage";
|
||||
import { classifyInternalChat } from "../ai/internal-chat-graph";
|
||||
import { runInternalChatWorkflow, createAppointmentToday } from "../ai/internal-chat-workflow";
|
||||
import { resolveAiProvider } from "../ai/llm-factory";
|
||||
|
||||
const CHAT_HISTORY_DIR = path.join(__dirname, "..", "..", "chat-history");
|
||||
|
||||
function getChatHistoryPath(userId: number): string {
|
||||
return path.join(CHAT_HISTORY_DIR, `user-${userId}.json`);
|
||||
}
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// GET /api/ai/settings
|
||||
@@ -323,4 +331,45 @@ router.post("/create-appointment-today", async (req: Request, res: Response): Pr
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Chat history persistence (JSON file per user) ──────────────────────────
|
||||
|
||||
router.get("/chat-history", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
const filePath = getChatHistoryPath(userId);
|
||||
if (!fs.existsSync(filePath)) return res.status(200).json({ messages: [] });
|
||||
const raw = fs.readFileSync(filePath, "utf-8");
|
||||
return res.status(200).json(JSON.parse(raw));
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: "Failed to load chat history", details: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
router.put("/chat-history", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
const { messages } = req.body;
|
||||
if (!Array.isArray(messages)) return res.status(400).json({ message: "messages array is required" });
|
||||
if (!fs.existsSync(CHAT_HISTORY_DIR)) fs.mkdirSync(CHAT_HISTORY_DIR, { recursive: true });
|
||||
fs.writeFileSync(getChatHistoryPath(userId), JSON.stringify({ messages }, null, 2));
|
||||
return res.status(200).json({ ok: true });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: "Failed to save chat history", details: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/chat-history", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
const filePath = getChatHistoryPath(userId);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
return res.status(200).json({ ok: true });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: "Failed to clear chat history", details: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user