Files
SuperCharged-Claude-Code-Up…/routes/sse-routes.js
uroma 55aafbae9a 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>
2026-01-22 14:43:05 +00:00

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;