import express from "express"; import path from "path"; import { createServer as createViteServer } from "vite"; import { v4 as uuidv4 } from "uuid"; import { WebSocketServer, WebSocket } from "ws"; import { createServer } from "http"; interface BrainstormIdea { id: string; sessionId: string; waveNumber: number; title: string; description: string; connectionToSeed: string; noveltyScore: number; parentIdeaId?: string; convertedToNoteId?: string; status: 'active' | 'dismissed' | 'converted'; position?: { x: number; y: number }; } interface BrainstormSession { id: string; seedIdea: string; sourceNoteId?: string; contextNoteIds?: string[]; createdAt: string; updatedAt: string; } // In-memory store const sessions: BrainstormSession[] = []; const ideas: BrainstormIdea[] = []; async function startServer() { const app = express(); const server = createServer(app); const wss = new WebSocketServer({ server }); const PORT = 3000; app.use(express.json()); // WebSocket logic const rooms = new Map>(); const roomUsers = new Map>(); wss.on('connection', (ws) => { let currentRoom: string | null = null; ws.on('message', (message) => { const data = JSON.parse(message.toString()); if (data.type === 'join') { const { sessionId, user } = data; currentRoom = sessionId; if (!rooms.has(sessionId)) rooms.set(sessionId, new Set()); if (!roomUsers.has(sessionId)) roomUsers.set(sessionId, new Map()); rooms.get(sessionId)!.add(ws); roomUsers.get(sessionId)!.set(ws, user || { id: uuidv4(), name: 'Guest' }); // Broadcast presence to the room const usersInRoom = Array.from(roomUsers.get(sessionId)!.values()); rooms.get(sessionId)!.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ type: 'presence', users: usersInRoom })); } }); console.log(`User ${user?.name || 'Guest'} joined session: ${sessionId}`); } if (data.type === 'idea_added' || data.type === 'idea_updated' || data.type === 'activity' || data.type === 'living_block_update') { if (currentRoom && rooms.has(currentRoom)) { rooms.get(currentRoom)!.forEach(client => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(data)); } }); } } }); ws.on('close', () => { if (currentRoom && rooms.has(currentRoom)) { rooms.get(currentRoom)!.delete(ws); roomUsers.get(currentRoom)!.delete(ws); // Update presence if (rooms.get(currentRoom)!.size > 0) { const usersInRoom = Array.from(roomUsers.get(currentRoom)!.values()); rooms.get(currentRoom)!.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ type: 'presence', users: usersInRoom })); } }); } else { rooms.delete(currentRoom); roomUsers.delete(currentRoom); } } }); }); // API Routes app.get("/api/health", (req, res) => { res.json({ status: "ok" }); }); // 1. Create session app.post("/api/brainstorm/sessions", (req, res) => { const { seedIdea, sourceNoteId, contextNoteIds } = req.body; const session: BrainstormSession = { id: uuidv4(), seedIdea, sourceNoteId, contextNoteIds, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; sessions.unshift(session); res.json(session); }); // 2. Add ideas to session app.post("/api/brainstorm/:sessionId/ideas", (req, res) => { const { sessionId } = req.params; const { ideas: newIdeasData } = req.body; const session = sessions.find(s => s.id === sessionId); if (!session) return res.status(404).json({ error: "Session not found" }); const newIdeas = newIdeasData.map((item: any) => ({ id: item.id || uuidv4(), sessionId, waveNumber: item.waveNumber, title: item.title, description: item.description, connectionToSeed: item.connectionToSeed, noveltyScore: item.noveltyScore, parentIdeaId: item.parentIdeaId, status: 'active' })); newIdeas.forEach((i: any) => ideas.push(i)); res.json(newIdeas); }); // 3. Get all sessions app.get("/api/brainstorm/sessions", (req, res) => { res.json(sessions); }); // 4. Get session with ideas app.get("/api/brainstorm/:sessionId", (req, res) => { const session = sessions.find(s => s.id === req.params.sessionId); if (!session) return res.status(404).json({ error: "Session not found" }); const sessionIdeas = ideas.filter(i => i.sessionId === session.id); res.json({ session, ideas: sessionIdeas }); }); // 5. Update idea (position, status) app.patch("/api/brainstorm/ideas/:ideaId", (req, res) => { const index = ideas.findIndex(i => i.id === req.params.ideaId); if (index === -1) return res.status(404).json({ error: "Idea not found" }); ideas[index] = { ...ideas[index], ...req.body }; res.json(ideas[index]); }); // Vite middleware for development if (process.env.NODE_ENV !== "production") { const vite = await createViteServer({ server: { middlewareMode: true }, appType: "spa", }); app.use(vite.middlewares); } else { const distPath = path.join(process.cwd(), 'dist'); app.use(express.static(distPath)); app.get('*', (req, res) => { res.sendFile(path.join(distPath, 'index.html')); }); } server.listen(PORT, "0.0.0.0", () => { console.log(`Server running on http://localhost:${PORT}`); }); } startServer();