From 375540f528cb79b52a8548a7483ebe69d9ea5ac4 Mon Sep 17 00:00:00 2001 From: sion123 <450702724@qq.com> Date: Thu, 7 May 2026 02:36:42 +0800 Subject: [PATCH] feat(web): add WebSocket chat handler with message persistence Co-Authored-By: Claude Opus 4.7 --- web/server/index.ts | 9 ++----- web/server/ws/chat.ts | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 web/server/ws/chat.ts diff --git a/web/server/index.ts b/web/server/index.ts index cbb6982..7de838c 100644 --- a/web/server/index.ts +++ b/web/server/index.ts @@ -8,6 +8,7 @@ import { promptsRouter } from './routes/prompts'; import { pipelineRouter } from './routes/pipeline'; import { assetsRouter } from './routes/assets'; import { configsRouter } from './routes/configs'; +import { handleChat } from './ws/chat'; const app = express(); const server = createServer(app); @@ -22,13 +23,7 @@ app.use('/api/pipeline', pipelineRouter); app.use('/api/assets', assetsRouter); app.use('/api/configs', configsRouter); -wss.on('connection', (ws) => { - // Placeholder - will be replaced in Task 3.1 - console.log('WebSocket client connected'); - ws.on('message', (data) => { - console.log('received:', data.toString()); - }); -}); +wss.on('connection', handleChat); const PORT = 3001; initDb(); diff --git a/web/server/ws/chat.ts b/web/server/ws/chat.ts new file mode 100644 index 0000000..345cfd4 --- /dev/null +++ b/web/server/ws/chat.ts @@ -0,0 +1,61 @@ +import { WebSocket } from 'ws'; +import { randomUUID } from 'crypto'; +import { getDb } from '../db'; + +export function handleChat(ws: WebSocket) { + let conversationId: string | null = null; + + ws.on('message', async (raw) => { + try { + const msg = JSON.parse(raw.toString()); + + if (msg.type === 'init') { + conversationId = msg.conversationId || randomUUID(); + const history = getDb().prepare( + 'SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at' + ).all(conversationId); + ws.send(JSON.stringify({ type: 'history', data: { conversationId, messages: history } })); + return; + } + + if (msg.type === 'chat') { + const { content } = msg; + const msgId = randomUUID(); + + getDb().prepare( + 'INSERT INTO messages (id, conversation_id, role, content) VALUES (?, ?, ?, ?)' + ).run(msgId, conversationId, 'user', content); + + ws.send(JSON.stringify({ type: 'message', data: { id: msgId, role: 'user', content } })); + + // Assistant echo (placeholder until LLM integration in Task 3.3) + const assistantId = randomUUID(); + const assistantContent = `收到你的消息:「${content}」。Agent 引擎正在启动中...`; + + getDb().prepare( + 'INSERT INTO messages (id, conversation_id, role, content) VALUES (?, ?, ?, ?)' + ).run(assistantId, conversationId, 'assistant', assistantContent); + + ws.send(JSON.stringify({ + type: 'message', + data: { id: assistantId, role: 'assistant', content: assistantContent }, + })); + } + + if (msg.type === 'create_conversation') { + const { title, accountId } = msg; + conversationId = randomUUID(); + getDb().prepare( + 'INSERT INTO conversations (id, title, account_id) VALUES (?, ?, ?)' + ).run(conversationId, title || '新对话', accountId || null); + ws.send(JSON.stringify({ type: 'conversation_created', data: { id: conversationId, title } })); + } + } catch (e) { + ws.send(JSON.stringify({ type: 'error', data: { message: (e as Error).message } })); + } + }); + + ws.on('close', () => { + // cleanup if needed + }); +}