mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-21 05:47:27 +00:00
- Add provider adapter layer (server/providers/) with registry pattern
- Claude, Cursor, Codex, Gemini adapters normalize native formats to NormalizedMessage
- Shared types.js defines ProviderAdapter interface and message kinds
- Registry enables polymorphic provider lookup
- Add unified REST endpoint: GET /api/sessions/:id/messages?provider=...
- Replaces four provider-specific message endpoints with one
- Delegates to provider adapters via registry
- Add frontend session-keyed store (useSessionStore)
- Per-session Map with serverMessages/realtimeMessages/merged
- Dedup by ID, stale threshold for re-fetch, background session accumulation
- No localStorage for messages — backend JSONL is source of truth
- Add normalizedToChatMessages converter (useChatMessages)
- Converts NormalizedMessage[] to existing ChatMessage[] UI format
- Wire unified store into ChatInterface, useChatSessionState, useChatRealtimeHandlers
- Session switch uses store cache for instant render
- Background WebSocket messages routed to correct session slot
62 lines
1.9 KiB
JavaScript
62 lines
1.9 KiB
JavaScript
/**
|
|
* Unified messages endpoint.
|
|
*
|
|
* GET /api/sessions/:sessionId/messages?provider=claude&projectName=foo&limit=50&offset=0
|
|
*
|
|
* Replaces the four provider-specific session message endpoints with a single route
|
|
* that delegates to the appropriate adapter via the provider registry.
|
|
*
|
|
* @module routes/messages
|
|
*/
|
|
|
|
import express from 'express';
|
|
import { getProvider, getAllProviders } from '../providers/registry.js';
|
|
|
|
const router = express.Router();
|
|
|
|
/**
|
|
* GET /api/sessions/:sessionId/messages
|
|
*
|
|
* Auth: authenticateToken applied at mount level in index.js
|
|
*
|
|
* Query params:
|
|
* provider - 'claude' | 'cursor' | 'codex' | 'gemini' (default: 'claude')
|
|
* projectName - required for claude provider
|
|
* projectPath - required for cursor provider (absolute path used for cwdId hash)
|
|
* limit - page size (omit or null for all)
|
|
* offset - pagination offset (default: 0)
|
|
*/
|
|
router.get('/:sessionId/messages', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.params;
|
|
const provider = req.query.provider || 'claude';
|
|
const projectName = req.query.projectName || '';
|
|
const projectPath = req.query.projectPath || '';
|
|
const limitParam = req.query.limit;
|
|
const limit = limitParam !== undefined && limitParam !== null && limitParam !== ''
|
|
? parseInt(limitParam, 10)
|
|
: null;
|
|
const offset = parseInt(req.query.offset || '0', 10);
|
|
|
|
const adapter = getProvider(provider);
|
|
if (!adapter) {
|
|
const available = getAllProviders().join(', ');
|
|
return res.status(400).json({ error: `Unknown provider: ${provider}. Available: ${available}` });
|
|
}
|
|
|
|
const result = await adapter.fetchHistory(sessionId, {
|
|
projectName,
|
|
projectPath,
|
|
limit,
|
|
offset,
|
|
});
|
|
|
|
return res.json(result);
|
|
} catch (error) {
|
|
console.error('Error fetching unified messages:', error);
|
|
return res.status(500).json({ error: 'Failed to fetch messages' });
|
|
}
|
|
});
|
|
|
|
export default router;
|