Fix project isolation: Make loadChatHistory respect active project sessions
- 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>
This commit is contained in:
138
routes/sse-routes.js
Normal file
138
routes/sse-routes.js
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user