Files
Momento/architectural-grid1/server.ts
Antigravity 52c4cb1dee
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Unit Tests & Build (push) Has been cancelled
feat: chunks recherche (snippets) + script migration
1. Recherche: fetchChunkSnippets() — après le classement RRF existant,
   récupère les passages précis qui matchent depuis NoteEmbeddingChunk.
   Pur affichage, AUCUN changement de classement.

2. Script migration: scripts/migrate-chunk-embeddings.ts
   Indexe toutes les notes existantes en fragments.
   Batch de 10, barre de progression.
   Usage: npx tsx scripts/migrate-chunk-embeddings.ts

3. Memory Echo chunk-level: à faire (US restante)
2026-06-20 17:07:38 +00:00

194 lines
5.8 KiB
TypeScript

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 = 4000;
app.use(express.json());
// WebSocket logic
const rooms = new Map<string, Set<WebSocket>>();
const roomUsers = new Map<string, Map<WebSocket, any>>();
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();