214 lines
7.8 KiB
TypeScript
214 lines
7.8 KiB
TypeScript
import { NextResponse } from "next/server"
|
|
import { auth } from "@/auth"
|
|
import { prisma } from "@/lib/prisma"
|
|
import { logger } from "@/lib/logger"
|
|
import {
|
|
knowledgeTemplatePutSchema,
|
|
mergeTemplateAnswers,
|
|
parseKnowledgeTemplateJson,
|
|
type KnowledgeTemplateStored,
|
|
} from "@/lib/kb/knowledgeTemplate"
|
|
import { replaceStructuredKnowledgeChunksForStore } from "@/lib/kb/structuredTemplateChunks"
|
|
import {
|
|
deleteDocumentKbForStore,
|
|
deleteFaqKbForStore,
|
|
deleteStructuredKbForStore,
|
|
} from "@/lib/kb/storeKbCleanup"
|
|
import { Prisma, type KnowledgeBaseMode } from "@prisma/client"
|
|
|
|
export async function GET(
|
|
_request: Request,
|
|
{ params }: { params: Promise<{ storeId: string }> },
|
|
) {
|
|
try {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
return NextResponse.json({ message: "Unauthorized" }, { status: 401 })
|
|
}
|
|
|
|
const { storeId } = await params
|
|
const store = await prisma.store.findFirst({
|
|
where: { id: storeId, userId: session.user.id },
|
|
select: {
|
|
id: true,
|
|
knowledgeBaseMode: true,
|
|
knowledgeTemplate: true,
|
|
},
|
|
})
|
|
|
|
if (!store) {
|
|
return NextResponse.json({ message: "Store not found" }, { status: 404 })
|
|
}
|
|
|
|
const [documentCount, faqCount, structuredChunkCount] = await Promise.all([
|
|
prisma.knowledgeDocument.count({ where: { storeId: store.id } }),
|
|
prisma.faqItem.count({ where: { storeId: store.id } }),
|
|
prisma.knowledgeChunk.count({
|
|
where: { storeId: store.id, sourceType: "STRUCTURED" },
|
|
}),
|
|
])
|
|
|
|
const normalizedTemplate = parseKnowledgeTemplateJson(store.knowledgeTemplate)
|
|
|
|
return NextResponse.json({
|
|
knowledgeBaseMode: store.knowledgeBaseMode,
|
|
knowledgeTemplate: normalizedTemplate ?? store.knowledgeTemplate,
|
|
counts: {
|
|
documents: documentCount,
|
|
faqs: faqCount,
|
|
structuredChunks: structuredChunkCount,
|
|
},
|
|
})
|
|
} catch (error) {
|
|
logger.error("GET knowledge-template error", error)
|
|
return NextResponse.json(
|
|
{ message: "Failed to load knowledge template" },
|
|
{ status: 500 },
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function PUT(
|
|
request: Request,
|
|
{ params }: { params: Promise<{ storeId: string }> },
|
|
) {
|
|
try {
|
|
const session = await auth()
|
|
if (!session?.user?.id) {
|
|
return NextResponse.json({ message: "Unauthorized" }, { status: 401 })
|
|
}
|
|
|
|
const { storeId } = await params
|
|
let store = await prisma.store.findFirst({
|
|
where: { id: storeId, userId: session.user.id },
|
|
})
|
|
|
|
if (!store) {
|
|
return NextResponse.json({ message: "Store not found" }, { status: 404 })
|
|
}
|
|
|
|
const json = await request.json().catch(() => null)
|
|
const parsed = knowledgeTemplatePutSchema.safeParse(json)
|
|
if (!parsed.success) {
|
|
return NextResponse.json(
|
|
{ message: "Invalid body", issues: parsed.error.flatten() },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
const body = parsed.data
|
|
const confirm = body.confirmDestructiveSwitch === true
|
|
|
|
const docCount = await prisma.knowledgeDocument.count({
|
|
where: { storeId: store.id },
|
|
})
|
|
const faqCount = await prisma.faqItem.count({ where: { storeId: store.id } })
|
|
const structuredCount = await prisma.knowledgeChunk.count({
|
|
where: { storeId: store.id, sourceType: "STRUCTURED" },
|
|
})
|
|
const hasTemplateJson = store.knowledgeTemplate != null
|
|
|
|
if (body.knowledgeBaseMode !== undefined) {
|
|
const target = body.knowledgeBaseMode as KnowledgeBaseMode
|
|
if (target !== store.knowledgeBaseMode) {
|
|
if (target === "STRUCTURED") {
|
|
const destructive = docCount > 0 || faqCount > 0
|
|
if (destructive && !confirm) {
|
|
return NextResponse.json(
|
|
{
|
|
message:
|
|
"Switching to structured knowledge will remove uploaded documents and FAQs. Send confirmDestructiveSwitch: true to proceed.",
|
|
code: "CONFIRM_REQUIRED",
|
|
},
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
if (destructive) {
|
|
await deleteDocumentKbForStore(prisma, store.id)
|
|
await deleteFaqKbForStore(prisma, store.id)
|
|
}
|
|
await prisma.store.update({
|
|
where: { id: store.id },
|
|
data: { knowledgeBaseMode: "STRUCTURED" },
|
|
})
|
|
} else if (target === "DOCUMENTS") {
|
|
const destructive = structuredCount > 0 || hasTemplateJson
|
|
if (destructive && !confirm) {
|
|
return NextResponse.json(
|
|
{
|
|
message:
|
|
"Switching to document-based knowledge will remove your structured profile and its embeddings. Send confirmDestructiveSwitch: true to proceed.",
|
|
code: "CONFIRM_REQUIRED",
|
|
},
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
await deleteStructuredKbForStore(prisma, store.id)
|
|
await prisma.store.update({
|
|
where: { id: store.id },
|
|
data: {
|
|
knowledgeBaseMode: "DOCUMENTS",
|
|
knowledgeTemplate: Prisma.JsonNull,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
store = await prisma.store.findFirstOrThrow({
|
|
where: { id: store.id, userId: session.user.id },
|
|
})
|
|
|
|
if (body.template !== undefined) {
|
|
if (store.knowledgeBaseMode === "DOCUMENTS") {
|
|
return NextResponse.json(
|
|
{
|
|
message:
|
|
"This store uses document-based knowledge. Switch to structured knowledge before editing the template.",
|
|
},
|
|
{ status: 409 },
|
|
)
|
|
}
|
|
|
|
const prev = parseKnowledgeTemplateJson(store.knowledgeTemplate)
|
|
const stored: KnowledgeTemplateStored = mergeTemplateAnswers(
|
|
prev,
|
|
body.template.answers as Record<string, string> | undefined,
|
|
)
|
|
|
|
const nextMode: KnowledgeBaseMode =
|
|
store.knowledgeBaseMode === "UNSET" ? "STRUCTURED" : store.knowledgeBaseMode
|
|
|
|
await prisma.store.update({
|
|
where: { id: store.id },
|
|
data: {
|
|
knowledgeBaseMode: nextMode,
|
|
knowledgeTemplate: stored as object,
|
|
},
|
|
})
|
|
|
|
await replaceStructuredKnowledgeChunksForStore(store.id)
|
|
}
|
|
|
|
const refreshed = await prisma.store.findFirst({
|
|
where: { id: store.id },
|
|
select: {
|
|
knowledgeBaseMode: true,
|
|
knowledgeTemplate: true,
|
|
},
|
|
})
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
knowledgeBaseMode: refreshed?.knowledgeBaseMode,
|
|
knowledgeTemplate: parseKnowledgeTemplateJson(refreshed?.knowledgeTemplate),
|
|
})
|
|
} catch (error) {
|
|
logger.error("PUT knowledge-template error", error)
|
|
return NextResponse.json(
|
|
{ message: "Failed to save knowledge template" },
|
|
{ status: 500 },
|
|
)
|
|
}
|
|
}
|