feat(web): add WebSocket chat handler with message persistence
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { promptsRouter } from './routes/prompts';
|
|||||||
import { pipelineRouter } from './routes/pipeline';
|
import { pipelineRouter } from './routes/pipeline';
|
||||||
import { assetsRouter } from './routes/assets';
|
import { assetsRouter } from './routes/assets';
|
||||||
import { configsRouter } from './routes/configs';
|
import { configsRouter } from './routes/configs';
|
||||||
|
import { handleChat } from './ws/chat';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = createServer(app);
|
const server = createServer(app);
|
||||||
@@ -22,13 +23,7 @@ app.use('/api/pipeline', pipelineRouter);
|
|||||||
app.use('/api/assets', assetsRouter);
|
app.use('/api/assets', assetsRouter);
|
||||||
app.use('/api/configs', configsRouter);
|
app.use('/api/configs', configsRouter);
|
||||||
|
|
||||||
wss.on('connection', (ws) => {
|
wss.on('connection', handleChat);
|
||||||
// Placeholder - will be replaced in Task 3.1
|
|
||||||
console.log('WebSocket client connected');
|
|
||||||
ws.on('message', (data) => {
|
|
||||||
console.log('received:', data.toString());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const PORT = 3001;
|
const PORT = 3001;
|
||||||
initDb();
|
initDb();
|
||||||
|
|||||||
61
web/server/ws/chat.ts
Normal file
61
web/server/ws/chat.ts
Normal file
@@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user