chat routes accept workspace API key (thin-client Change 8.1)
This commit is contained in:
@@ -15,8 +15,8 @@
|
|||||||
* data: {"type":"error","error":"..."}
|
* data: {"type":"error","error":"..."}
|
||||||
*/
|
*/
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { authSession } from "@/lib/auth/session-server";
|
import { requireWorkspacePrincipal } from "@/lib/auth/workspace-auth";
|
||||||
import { query } from "@/lib/db-postgres";
|
import { query, queryOne } from "@/lib/db-postgres";
|
||||||
import { callVibnChat } from "@/lib/ai/vibn-chat-model";
|
import { callVibnChat } from "@/lib/ai/vibn-chat-model";
|
||||||
import { VIBN_TOOL_DEFINITIONS, executeMcpTool } from "@/lib/ai/vibn-tools";
|
import { VIBN_TOOL_DEFINITIONS, executeMcpTool } from "@/lib/ai/vibn-tools";
|
||||||
import {
|
import {
|
||||||
@@ -394,10 +394,17 @@ function lastToolResultsHadFailure(messages: ChatMessage[], lookback = 3) {
|
|||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
await ensureChatTables();
|
await ensureChatTables();
|
||||||
|
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) {
|
if (principal instanceof NextResponse) return principal;
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) {
|
||||||
|
return NextResponse.json({ error: "Unauthorized user" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
let body: {
|
let body: {
|
||||||
thread_id: string;
|
thread_id: string;
|
||||||
@@ -428,7 +435,7 @@ export async function POST(request: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const email = session.user.email;
|
const email = sessionEmail;
|
||||||
|
|
||||||
// Verify thread belongs to user, and capture its project scope (if any).
|
// Verify thread belongs to user, and capture its project scope (if any).
|
||||||
const threads = await query<{ id: string; project_id: string | null }>(
|
const threads = await query<{ id: string; project_id: string | null }>(
|
||||||
|
|||||||
@@ -4,18 +4,25 @@
|
|||||||
* DELETE /api/chat/threads/[id] — delete a thread
|
* DELETE /api/chat/threads/[id] — delete a thread
|
||||||
*/
|
*/
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { authSession } from '@/lib/auth/session-server';
|
import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth';
|
||||||
import { query } from '@/lib/db-postgres';
|
import { query, queryOne } from '@/lib/db-postgres';
|
||||||
|
|
||||||
export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
if (principal instanceof NextResponse) return principal;
|
||||||
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) return NextResponse.json({ error: 'Unauthorized user' }, { status: 401 });
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
|
|
||||||
const threads = await query<any>(
|
const threads = await query<any>(
|
||||||
`SELECT id, data, created_at, updated_at FROM fs_chat_threads WHERE id = $1 AND user_id = $2`,
|
`SELECT id, data, created_at, updated_at FROM fs_chat_threads WHERE id = $1 AND user_id = $2`,
|
||||||
[id, session.user.email],
|
[id, sessionEmail],
|
||||||
);
|
);
|
||||||
if (!threads.length) return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
if (!threads.length) return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||||
|
|
||||||
@@ -32,8 +39,15 @@ export async function GET(request: Request, { params }: { params: Promise<{ id:
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
if (principal instanceof NextResponse) return principal;
|
||||||
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) return NextResponse.json({ error: 'Unauthorized user' }, { status: 401 });
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const { title } = await request.json().catch(() => ({}));
|
const { title } = await request.json().catch(() => ({}));
|
||||||
@@ -41,16 +55,23 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
|
|||||||
|
|
||||||
await query(
|
await query(
|
||||||
`UPDATE fs_chat_threads SET data = data || $3, updated_at = NOW() WHERE id = $1 AND user_id = $2`,
|
`UPDATE fs_chat_threads SET data = data || $3, updated_at = NOW() WHERE id = $1 AND user_id = $2`,
|
||||||
[id, session.user.email, JSON.stringify({ title })],
|
[id, sessionEmail, JSON.stringify({ title })],
|
||||||
);
|
);
|
||||||
return NextResponse.json({ ok: true });
|
return NextResponse.json({ ok: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
if (principal instanceof NextResponse) return principal;
|
||||||
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) return NextResponse.json({ error: 'Unauthorized user' }, { status: 401 });
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
await query(`DELETE FROM fs_chat_threads WHERE id = $1 AND user_id = $2`, [id, session.user.email]);
|
await query(`DELETE FROM fs_chat_threads WHERE id = $1 AND user_id = $2`, [id, sessionEmail]);
|
||||||
return NextResponse.json({ ok: true });
|
return NextResponse.json({ ok: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
* after deploy (no manual migration needed).
|
* after deploy (no manual migration needed).
|
||||||
*/
|
*/
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { authSession } from '@/lib/auth/session-server';
|
import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth';
|
||||||
import { query } from '@/lib/db-postgres';
|
import { query, queryOne } from '@/lib/db-postgres';
|
||||||
|
|
||||||
let chatTablesReady = false;
|
let chatTablesReady = false;
|
||||||
async function ensureChatTables() {
|
async function ensureChatTables() {
|
||||||
@@ -54,8 +54,15 @@ async function ensureChatTables() {
|
|||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
await ensureChatTables();
|
await ensureChatTables();
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
if (principal instanceof NextResponse) return principal;
|
||||||
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) return NextResponse.json({ error: 'Unauthorized user' }, { status: 401 });
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const workspace = searchParams.get('workspace') || '';
|
const workspace = searchParams.get('workspace') || '';
|
||||||
@@ -90,8 +97,8 @@ export async function GET(request: Request) {
|
|||||||
WHERE t.user_id = $1 AND t.workspace = $2 AND t.project_id IS NULL
|
WHERE t.user_id = $1 AND t.workspace = $2 AND t.project_id IS NULL
|
||||||
ORDER BY t.updated_at DESC LIMIT 50`;
|
ORDER BY t.updated_at DESC LIMIT 50`;
|
||||||
const args = projectId
|
const args = projectId
|
||||||
? [session.user.email, workspace, projectId]
|
? [sessionEmail, workspace, projectId]
|
||||||
: [session.user.email, workspace];
|
: [sessionEmail, workspace];
|
||||||
|
|
||||||
const rows = await query<any>(sql, args);
|
const rows = await query<any>(sql, args);
|
||||||
|
|
||||||
@@ -110,8 +117,15 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
await ensureChatTables();
|
await ensureChatTables();
|
||||||
const session = await authSession();
|
const principal = await requireWorkspacePrincipal(request);
|
||||||
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
if (principal instanceof NextResponse) return principal;
|
||||||
|
|
||||||
|
const userRow = await queryOne<{ data: any }>(
|
||||||
|
`SELECT data FROM fs_users WHERE id = $1 LIMIT 1`,
|
||||||
|
[principal.userId]
|
||||||
|
);
|
||||||
|
if (!userRow?.data?.email) return NextResponse.json({ error: 'Unauthorized user' }, { status: 401 });
|
||||||
|
const sessionEmail = userRow.data.email;
|
||||||
|
|
||||||
const { workspace, title, projectId } = await request.json().catch(() => ({}));
|
const { workspace, title, projectId } = await request.json().catch(() => ({}));
|
||||||
if (!workspace) return NextResponse.json({ error: 'workspace required' }, { status: 400 });
|
if (!workspace) return NextResponse.json({ error: 'workspace required' }, { status: 400 });
|
||||||
@@ -125,17 +139,17 @@ export async function POST(request: Request) {
|
|||||||
`SELECT p.id FROM fs_projects p
|
`SELECT p.id FROM fs_projects p
|
||||||
JOIN fs_users u ON u.id = p.user_id
|
JOIN fs_users u ON u.id = p.user_id
|
||||||
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
|
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
|
||||||
[projectId, session.user.email],
|
[projectId, sessionEmail],
|
||||||
);
|
);
|
||||||
if (owned.length > 0) safeProjectId = projectId;
|
if (owned.length > 0) safeProjectId = projectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = await query<any>(
|
const rows = await query<any>(
|
||||||
`INSERT INTO fs_chat_threads (user_id, workspace, project_id, data)
|
`INSERT INTO fs_chat_threads (user_id, workspace, project_id, data)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4)
|
||||||
RETURNING id, project_id, data, created_at, updated_at`,
|
RETURNING id, project_id, data, created_at, updated_at`,
|
||||||
[
|
[
|
||||||
session.user.email,
|
sessionEmail,
|
||||||
workspace,
|
workspace,
|
||||||
safeProjectId,
|
safeProjectId,
|
||||||
JSON.stringify({ title: title || 'New conversation', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }),
|
JSON.stringify({ title: title || 'New conversation', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }),
|
||||||
|
|||||||
Reference in New Issue
Block a user