- Modified loadChatHistory() to check for active project before fetching all sessions - When active project exists, use project.sessions instead of fetching from API - Added detailed console logging to debug session filtering - This prevents ALL sessions from appearing in every project's sidebar Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
139 lines
3.8 KiB
JavaScript
139 lines
3.8 KiB
JavaScript
/**
|
|
* SSE Routes
|
|
*
|
|
* Server-Sent Events endpoints for real-time session event streaming.
|
|
*/
|
|
|
|
const express = require('express');
|
|
const sseManager = require('../services/sse-manager');
|
|
const { validateSessionId } = require('../middleware/validation');
|
|
|
|
const router = express.Router();
|
|
|
|
/**
|
|
* SSE endpoint for session events
|
|
* GET /api/session/:sessionId/events
|
|
*
|
|
* Establishes a Server-Sent Events connection for streaming real-time
|
|
* session events to the client.
|
|
*
|
|
* Events:
|
|
* - connected: Initial connection confirmation
|
|
* - session-output: Output from Claude Code process
|
|
* - session-error: Error from session
|
|
* - session-status: Session status update
|
|
* - operations-detected: Claude operations detected in response
|
|
* - operations-executed: Operations execution results
|
|
* - approval-request: Command approval requested
|
|
* - approval-confirmed: Command approval confirmed/expired
|
|
*
|
|
* Example:
|
|
* const eventSource = new EventSource('/api/session/session-123/events');
|
|
* eventSource.addEventListener('session-output', (e) => {
|
|
* const data = JSON.parse(e.data);
|
|
* console.log('Output:', data.content);
|
|
* });
|
|
*/
|
|
router.get('/session/:sessionId/events', validateSessionId, (req, res) => {
|
|
const { sessionId } = req.params;
|
|
|
|
console.log(`[SSERoutes] New SSE connection request for session ${sessionId}`);
|
|
console.log(`[SSERoutes] Client IP: ${req.ip}`);
|
|
console.log(`[SSERoutes] User-Agent: ${req.get('User-Agent')?.substring(0, 100)}`);
|
|
|
|
// Add SSE connection (response is kept open for streaming)
|
|
sseManager.addConnection(sessionId, res, req);
|
|
|
|
// Note: No response sent here - connection stays open for SSE streaming
|
|
});
|
|
|
|
/**
|
|
* Get connection status for a session
|
|
* GET /api/session/:sessionId/events/status
|
|
*
|
|
* Returns information about active SSE connections for a session.
|
|
*
|
|
* Response:
|
|
* {
|
|
* "sessionId": "session-123",
|
|
* "activeConnections": 2,
|
|
* "timestamp": 1234567890
|
|
* }
|
|
*/
|
|
router.get('/session/:sessionId/events/status', validateSessionId, (req, res) => {
|
|
const { sessionId } = req.params;
|
|
|
|
const activeConnections = sseManager.getConnectionCount(sessionId);
|
|
|
|
res.json({
|
|
sessionId,
|
|
activeConnections,
|
|
timestamp: Date.now()
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Get global SSE stats (admin endpoint)
|
|
* GET /api/sse/stats
|
|
*
|
|
* Returns overall SSE connection statistics.
|
|
*
|
|
* Response:
|
|
* {
|
|
* "totalSessions": 5,
|
|
* "totalConnections": 12,
|
|
* "sessions": { "session-1": 2, "session-2": 1, ... },
|
|
* "totalCreated": 50,
|
|
* "totalClosed": 38,
|
|
* "activeHeartbeats": 12
|
|
* }
|
|
*/
|
|
router.get('/sse/stats', (req, res) => {
|
|
const stats = sseManager.getStats();
|
|
|
|
res.json(stats);
|
|
});
|
|
|
|
/**
|
|
* Test SSE endpoint (for development/testing)
|
|
* GET /api/sse/test
|
|
*
|
|
* Sends test events every second for 10 seconds.
|
|
* Only available in development mode.
|
|
*/
|
|
router.get('/sse/test', (req, res) => {
|
|
if (process.env.NODE_ENV === 'production') {
|
|
return res.status(404).json({ error: 'Not found' });
|
|
}
|
|
|
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
res.setHeader('Cache-Control', 'no-cache');
|
|
res.setHeader('Connection', 'keep-alive');
|
|
res.flushHeaders();
|
|
|
|
let count = 0;
|
|
const maxEvents = 10;
|
|
|
|
const interval = setInterval(() => {
|
|
count++;
|
|
|
|
res.write(`event: test\n`);
|
|
res.write(`data: ${JSON.stringify({ message: `Test event ${count}`, count, timestamp: Date.now() })}\n`);
|
|
res.write(`id: ${Date.now()}\n`);
|
|
res.write('\n');
|
|
|
|
if (count >= maxEvents) {
|
|
clearInterval(interval);
|
|
res.write('event: end\n');
|
|
res.write('data: {"message":"Test complete"}\n\n');
|
|
res.end();
|
|
}
|
|
}, 1000);
|
|
|
|
req.on('close', () => {
|
|
clearInterval(interval);
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|