fix: restore brainstorming feature with missing socket server and real-time events
Some checks failed
CI / Lint, Test & Build (push) Failing after 7m48s
CI / Deploy production (on server) (push) Has been cancelled

This commit is contained in:
Antigravity
2026-05-19 20:07:56 +00:00
parent 66c6f7ee8f
commit fdb148144e
15 changed files with 500 additions and 2 deletions

View File

@@ -0,0 +1,23 @@
FROM node:22-bookworm-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
&& rm -rf /var/lib/apt/lists/*
COPY package.json package-lock.json* ./
RUN npm install
# We only need the socket server and dependencies
COPY socket-server.ts ./
COPY tsconfig.json ./
# Environment defaults
ENV NODE_ENV=production
ENV SOCKET_PORT=3002
ENV SOCKET_HTTP_PORT=3003
# Run with tsx (included in devDependencies but we need it at runtime here)
# Alternatively, we could build it to JS, but tsx is easier for this standalone file.
CMD ["npx", "tsx", "socket-server.ts"]

View File

@@ -0,0 +1,35 @@
FROM node:22-bookworm-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
&& rm -rf /var/lib/apt/lists/*
# In prebuilt mode, we expect node_modules to be partially there or we use the standalone's ones
# Actually, the artifact has node_modules/.prisma etc. but not the full node_modules.
# However, the standalone folder HAS node_modules.
# But we need tsx and socket.io.
# So we still need a minimal npm install or just use the pre-installed ones if we can.
# Let's keep it simple: if it's prebuilt, we just copy what we need.
# But tsx is a devDependency.
# Better to just use the full Dockerfile.socket for now as it's more reliable.
# "tsx" is not in the standalone output.
# Wait, I can just use the same strategy as memento-note:
# if ARTIFACT_TGZ is present, we already tar'ed the files.
# But we still need to run them.
COPY socket-server.ts ./
COPY tsconfig.json ./
COPY package.json package-lock.json* ./
# We still need to install dependencies because standalone doesn't have all of them (like tsx)
RUN npm install --only=production && npm install tsx
ENV NODE_ENV=production
ENV SOCKET_PORT=3002
ENV SOCKET_HTTP_PORT=3003
CMD ["npx", "tsx", "socket-server.ts"]

View File

@@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from 'next/server'
import prisma from '@/lib/prisma'
import { auth } from '@/auth'
import { z } from 'zod'
import { logActivity } from '@/lib/brainstorm-collab'
import { emitToSession } from '@/lib/socket-emit'
const convertSchema = z.object({
ideaId: z.string().min(1),
@@ -153,6 +155,15 @@ export async function POST(
await prisma.$transaction(tagPromises)
await logActivity(sessionId, 'idea_converted', session.user.id, { ideaTitle: idea.title, ideaId: idea.id, noteId: note.id })
await emitToSession(sessionId, 'activity:new', {
action: 'idea_converted',
userId: session.user.id,
userName: session.user.name || 'Guest',
details: { ideaTitle: idea.title, ideaId: idea.id, noteId: note.id }
})
return NextResponse.json({ success: true, data: note }, { status: 201 })
} catch (error: any) {
if (error instanceof z.ZodError) {

View File

@@ -3,6 +3,7 @@ import prisma from '@/lib/prisma'
import { auth } from '@/auth'
import { z } from 'zod'
import { verifyParticipant, logActivity, captureSnapshot } from '@/lib/brainstorm-collab'
import { emitToSession } from '@/lib/socket-emit'
const dismissSchema = z.object({
ideaId: z.string().min(1),
@@ -56,6 +57,14 @@ export async function POST(
await logActivity(sessionId, 'idea_dismissed', session.user.id, { ideaTitle: idea.title })
await emitToSession(sessionId, 'idea:dismissed', { ideaId, userId: session.user.id })
await emitToSession(sessionId, 'activity:new', {
action: 'idea_dismissed',
userId: session.user.id,
userName: session.user.name || 'Guest',
details: { ideaTitle: idea.title, ideaId }
})
await captureSnapshot(sessionId, `Dismissed: ${idea.title}`).catch(() => {})
return NextResponse.json({ success: true })

View File

@@ -15,7 +15,9 @@ import {
resolveAiContextUserId,
sanitizeNotesForGuest,
captureSnapshot,
logActivity,
} from '@/lib/brainstorm-collab'
import { emitToSession } from '@/lib/socket-emit'
const expandSchema = z.object({
ideaId: z.string().min(1),
@@ -332,6 +334,17 @@ export async function POST(
for (const idea of updatedSession?.ideas || []) { (idea as any).creator = (idea as any).createdBy ? cm.get((idea as any).createdBy) || null : null }
}
await logActivity(sessionId, 'wave_generated', session.user.id, { count: newIdeas.length, parentIdeaId: ideaId })
await emitToSession(sessionId, 'activity:new', {
action: 'wave_generated',
userId: session.user.id,
userName: session.user.name || 'Guest',
details: { count: newIdeas.length, parentIdeaId: ideaId }
})
// Trigger refetch for all participants
await emitToSession(sessionId, 'idea:added', {})
await captureSnapshot(sessionId, `Wave expanded: ${parentIdea.title}`).catch(() => {})
return NextResponse.json({ success: true, data: updatedSession })

View File

@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from 'next/server'
import prisma from '@/lib/prisma'
import { auth } from '@/auth'
import { logActivity } from '@/lib/brainstorm-collab'
import { emitToSession } from '@/lib/socket-emit'
export async function POST(
request: NextRequest,
@@ -80,6 +82,15 @@ export async function POST(
const dry = Array.from(noteImpactMap.values()).filter(n => n.acceptedCount === 0 && n.dismissedCount > 0).length
const totalRefs = Array.from(noteImpactMap.values()).length
await logActivity(sessionId, 'session_finalized', session.user.id, { notesEnriched: fruitful, notesMarkedDry: dry })
await emitToSession(sessionId, 'activity:new', {
action: 'session_finalized',
userId: session.user.id,
userName: session.user.name || 'Guest',
details: { notesEnriched: fruitful, notesMarkedDry: dry }
})
return NextResponse.json({
success: true,
impact: {

View File

@@ -177,6 +177,13 @@ export async function POST(
await logActivity(sessionId, 'manual_idea', session.user.id, { ideaTitle: title, ideaId: idea.id })
await emitToSession(sessionId, 'activity:new', {
action: 'manual_idea',
userId: session.user.id,
userName: session.user.name || 'Guest',
details: { ideaTitle: title, ideaId: idea.id }
})
await captureSnapshot(sessionId, `Manual idea: ${title}`).catch(() => {})
// [UPDATE - TEMPS RÉEL] Retourner immédiatement, enrichissement IA en arrière-plan

View File

@@ -17,7 +17,7 @@ export interface ActivityEvent {
details: any
}
const SOCKET_URL = process.env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3001'
const SOCKET_URL = process.env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3002'
export function useBrainstormSocket(
sessionId: string | null,