fix: restore brainstorming feature with missing socket server and real-time events
This commit is contained in:
23
memento-note/Dockerfile.socket
Normal file
23
memento-note/Dockerfile.socket
Normal 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"]
|
||||
35
memento-note/Dockerfile.socket.prebuilt
Normal file
35
memento-note/Dockerfile.socket.prebuilt
Normal 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"]
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user