Files
vibn-frontend/app/api/chat/threads/route.ts
Mark Henderson 5e07bbf39d Add Vibn AI chat panel powered by Gemini 3.1 Pro
- 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
2026-04-27 15:40:32 -07:00

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 } });
}