- Right-docked chat panel on all workspace pages ([workspace]/layout.tsx) - Streaming SSE responses with Gemini 3.1 Pro preview via generativelanguage API - Full tool-calling loop (up to 6 rounds): deploys apps, lists projects, registers domains, fetches logs — all via existing MCP dispatcher - Persistent conversation history: fs_chat_threads + fs_chat_messages tables (Postgres) - Thread management: create, list, rename (auto-title from first message), delete - Panel collapses to a tab; open state persisted to localStorage - Read-only mode hint when no MCP token is present - Graceful content margin shift when panel is open Made-with: Cursor
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
/**
|
|
* GET /api/chat/threads — list user's threads
|
|
* POST /api/chat/threads — create a new thread
|
|
*/
|
|
import { NextResponse } from 'next/server';
|
|
import { authSession } from '@/lib/auth/session-server';
|
|
import { query } from '@/lib/db-postgres';
|
|
|
|
export async function GET(request: Request) {
|
|
const session = await authSession();
|
|
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
|
|
const { searchParams } = new URL(request.url);
|
|
const workspace = searchParams.get('workspace') || '';
|
|
|
|
const rows = await query<any>(
|
|
`SELECT id, data, created_at, updated_at
|
|
FROM fs_chat_threads
|
|
WHERE user_id = $1 AND workspace = $2
|
|
ORDER BY updated_at DESC
|
|
LIMIT 50`,
|
|
[session.user.email, workspace],
|
|
);
|
|
|
|
return NextResponse.json({
|
|
threads: rows.map((r: any) => ({
|
|
id: r.id,
|
|
title: r.data?.title || 'New conversation',
|
|
updatedAt: r.updated_at,
|
|
createdAt: r.created_at,
|
|
})),
|
|
});
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
const session = await authSession();
|
|
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
|
|
const { workspace, title } = await request.json().catch(() => ({}));
|
|
if (!workspace) return NextResponse.json({ error: 'workspace required' }, { status: 400 });
|
|
|
|
const rows = await query<any>(
|
|
`INSERT INTO fs_chat_threads (user_id, workspace, data)
|
|
VALUES ($1, $2, $3)
|
|
RETURNING id, data, created_at, updated_at`,
|
|
[
|
|
session.user.email,
|
|
workspace,
|
|
JSON.stringify({ title: title || 'New conversation', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }),
|
|
],
|
|
);
|
|
|
|
const r = rows[0];
|
|
return NextResponse.json({ thread: { id: r.id, title: r.data?.title || 'New conversation', createdAt: r.created_at, updatedAt: r.updated_at } });
|
|
}
|