()
| 672 | }; |
| 673 | |
| 674 | export function createAddieChatRouter(): { pageRouter: Router; apiRouter: Router } { |
| 675 | const pageRouter = Router(); |
| 676 | const apiRouter = Router(); |
| 677 | |
| 678 | // Enable CORS for all API routes (for native app support) |
| 679 | apiRouter.use(cors(chatCorsOptions)); |
| 680 | |
| 681 | // Initialize client after server starts (deferred to avoid blocking startup with sync I/O) |
| 682 | setTimeout(() => { |
| 683 | initializeChatClient().catch((err) => { |
| 684 | logger.error({ err }, "Failed to initialize Addie chat client"); |
| 685 | }); |
| 686 | }, 5000); |
| 687 | |
| 688 | // ========================================================================= |
| 689 | // PAGE ROUTES (mounted at /chat) |
| 690 | // ========================================================================= |
| 691 | |
| 692 | // GET / - Serve the chat page (mounted at /chat, so this serves /chat) |
| 693 | pageRouter.get("/", optionalAuth, (req, res) => { |
| 694 | // Video call iframe needs camera, microphone, and autoplay permissions |
| 695 | res.setHeader("Permissions-Policy", "camera=*, microphone=*, autoplay=*"); |
| 696 | serveHtmlWithConfig(req, res, "chat.html").catch((err) => { |
| 697 | logger.error({ err }, "Error serving chat page"); |
| 698 | res.status(500).send("Internal server error"); |
| 699 | }); |
| 700 | }); |
| 701 | |
| 702 | // ========================================================================= |
| 703 | // API ROUTES (mounted at /api/addie/chat) |
| 704 | // ========================================================================= |
| 705 | |
| 706 | // POST /api/addie/chat - Send a message and get a response |
| 707 | // optionalAuth runs first so rate limiters can check auth status |
| 708 | apiRouter.post("/", optionalAuth, chatRateLimiter, anonymousDailyLimiter, async (req, res) => { |
| 709 | const startTime = Date.now(); |
| 710 | const threadService = getThreadService(); |
| 711 | |
| 712 | try { |
| 713 | if (!initialized || !claudeClient) { |
| 714 | return res.status(503).json({ |
| 715 | error: "Service unavailable", |
| 716 | message: "Addie is not configured. Please set ANTHROPIC_API_KEY.", |
| 717 | }); |
| 718 | } |
| 719 | |
| 720 | const { message, conversation_id, user_name } = req.body; |
| 721 | |
| 722 | if (!message || typeof message !== "string") { |
| 723 | return res.status(400).json({ error: "Message is required" }); |
| 724 | } |
| 725 | |
| 726 | // Sanitize input |
| 727 | const inputValidation = sanitizeInput(message); |
| 728 | if (inputValidation.flagged) { |
| 729 | logger.warn({ reason: inputValidation.reason }, "Addie Chat: Input flagged"); |
| 730 | } |
| 731 |
no test coverage detected