DB-backed workspace + LangGraph agent

This commit is contained in:
Ruslan Bakiev
2026-02-18 13:56:35 +07:00
parent a8db021597
commit efa0b79c4c
36 changed files with 2125 additions and 468 deletions

View File

@@ -1,5 +1,6 @@
import { prisma } from "./prisma";
import type { H3Event } from "h3";
import { getCookie, setCookie, deleteCookie, getHeader } from "h3";
import { prisma } from "./prisma";
export type AuthContext = {
teamId: string;
@@ -7,51 +8,85 @@ export type AuthContext = {
conversationId: string;
};
// Minimal temporary auth: pick from headers or auto-provision a default team/user.
const COOKIE_USER = "cf_user";
const COOKIE_TEAM = "cf_team";
const COOKIE_CONV = "cf_conv";
function cookieOpts() {
return {
httpOnly: true,
sameSite: "lax" as const,
path: "/",
secure: process.env.NODE_ENV === "production",
};
}
export function clearAuthSession(event: H3Event) {
deleteCookie(event, COOKIE_USER, { path: "/" });
deleteCookie(event, COOKIE_TEAM, { path: "/" });
deleteCookie(event, COOKIE_CONV, { path: "/" });
}
export function setSession(event: H3Event, ctx: AuthContext) {
setCookie(event, COOKIE_USER, ctx.userId, cookieOpts());
setCookie(event, COOKIE_TEAM, ctx.teamId, cookieOpts());
setCookie(event, COOKIE_CONV, ctx.conversationId, cookieOpts());
}
export async function getAuthContext(event: H3Event): Promise<AuthContext> {
const cookieUser = getCookie(event, COOKIE_USER)?.trim();
const cookieTeam = getCookie(event, COOKIE_TEAM)?.trim();
const cookieConv = getCookie(event, COOKIE_CONV)?.trim();
// Temporary compatibility: allow passing via headers for debugging/dev tools.
const hdrTeam = getHeader(event, "x-team-id")?.trim();
const hdrUser = getHeader(event, "x-user-id")?.trim();
const hdrConv = getHeader(event, "x-conversation-id")?.trim();
// Ensure default team/user exist.
const user =
(hdrUser ? await prisma.user.findUnique({ where: { id: hdrUser } }) : null) ??
(await prisma.user.upsert({
where: { id: "demo-user" },
update: { email: "demo@clientsflow.local", name: "Demo User" },
create: { id: "demo-user", email: "demo@clientsflow.local", name: "Demo User" },
}));
const hasAnySession = Boolean(cookieUser || cookieTeam || cookieConv || hdrTeam || hdrUser || hdrConv);
if (!hasAnySession) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}
const team =
(hdrTeam
? await prisma.team.findUnique({ where: { id: hdrTeam } })
: null) ??
(await prisma.team.upsert({
where: { id: "demo-team" },
update: { name: "Demo Team" },
create: { id: "demo-team", name: "Demo Team" },
}));
const userId = cookieUser || hdrUser;
const teamId = cookieTeam || hdrTeam;
const conversationId = cookieConv || hdrConv;
if (!userId || !teamId || !conversationId) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}
const user = await prisma.user.findUnique({ where: { id: userId } });
const team = await prisma.team.findUnique({ where: { id: teamId } });
const conv = await prisma.chatConversation.findUnique({ where: { id: conversationId } });
if (!user || !team || !conv) {
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
}
return { teamId: team.id, userId: user.id, conversationId: conv.id };
}
export async function ensureDemoAuth() {
const user = await prisma.user.upsert({
where: { id: "demo-user" },
update: { email: "demo@clientsflow.local", name: "Demo User" },
create: { id: "demo-user", email: "demo@clientsflow.local", name: "Demo User" },
});
const team = await prisma.team.upsert({
where: { id: "demo-team" },
update: { name: "Demo Team" },
create: { id: "demo-team", name: "Demo Team" },
});
await prisma.teamMember.upsert({
where: { teamId_userId: { teamId: team.id, userId: user.id } },
update: {},
create: { teamId: team.id, userId: user.id, role: "OWNER" },
});
const conversation =
(hdrConv
? await prisma.chatConversation.findUnique({ where: { id: hdrConv } })
: null) ??
(await prisma.chatConversation.upsert({
where: { id: `pilot-${team.id}` },
update: {},
create: {
id: `pilot-${team.id}`,
teamId: team.id,
createdByUserId: user.id,
title: "Pilot",
},
}));
return { teamId: team.id, userId: user.id, conversationId: conversation.id };
const conv = await prisma.chatConversation.upsert({
where: { id: `pilot-${team.id}` },
update: {},
create: { id: `pilot-${team.id}`, teamId: team.id, createdByUserId: user.id, title: "Pilot" },
});
return { teamId: team.id, userId: user.id, conversationId: conv.id };
}