import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { updateKnowledgeBaseContract } from '@/lib/api/contracts/knowledge' import { parseRequest } from '@/lib/api/server' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { PlatformEvents } from '@/lib/core/telemetry' import { generateRequestId } from '@/lib/core/utils/request' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { deleteKnowledgeBase, getKnowledgeBaseById, KnowledgeBaseConflictError, KnowledgeBasePermissionError, updateKnowledgeBase, } from '@/lib/knowledge/service' import { checkKnowledgeBaseAccess, checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils' const logger = createLogger('KnowledgeBaseByIdAPI') export const GET = withRouteHandler( async (_request: NextRequest, { params }: { params: Promise<{ id: string }> }) => { const requestId = generateRequestId() const { id } = await params try { const auth = await checkSessionOrInternalAuth(_request, { requireWorkflowId: false }) if (!auth.success || !auth.userId) { logger.warn(`[${requestId}] Unauthorized knowledge base access attempt`) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = auth.userId const accessCheck = await checkKnowledgeBaseAccess(id, userId) if (!accessCheck.hasAccess) { if ('notFound' in accessCheck && accessCheck.notFound) { logger.warn(`[${requestId}] Knowledge base not found: ${id}`) return NextResponse.json({ error: 'Knowledge base not found' }, { status: 404 }) } logger.warn( `[${requestId}] User ${userId} attempted to access unauthorized knowledge base ${id}` ) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const knowledgeBaseData = await getKnowledgeBaseById(id) if (!knowledgeBaseData) { return NextResponse.json({ error: 'Knowledge base not found' }, { status: 404 }) } logger.info(`[${requestId}] Retrieved knowledge base: ${id} for user ${userId}`) return NextResponse.json({ success: true, data: knowledgeBaseData, }) } catch (error) { logger.error(`[${requestId}] Error fetching knowledge base`, error) return NextResponse.json({ error: 'Failed to fetch knowledge base' }, { status: 500 }) } } ) export const PUT = withRouteHandler( async (req: NextRequest, context: { params: Promise<{ id: string }> }) => { const requestId = generateRequestId() const { id } = await context.params try { const auth = await checkSessionOrInternalAuth(req, { requireWorkflowId: false }) if (!auth.success || !auth.userId) { logger.warn(`[${requestId}] Unauthorized knowledge base update attempt`) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = auth.userId const accessCheck = await checkKnowledgeBaseWriteAccess(id, userId) if (!accessCheck.hasAccess) { if ('notFound' in accessCheck && accessCheck.notFound) { logger.warn(`[${requestId}] Knowledge base not found: ${id}`) return NextResponse.json({ error: 'Knowledge base not found' }, { status: 404 }) } logger.warn( `[${requestId}] User ${userId} attempted to update unauthorized knowledge base ${id}` ) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const parsed = await parseRequest(updateKnowledgeBaseContract, req, context) if (!parsed.success) return parsed.response const validatedData = parsed.data.body const updatedKnowledgeBase = await updateKnowledgeBase( id, { name: validatedData.name, description: validatedData.description, workspaceId: validatedData.workspaceId, chunkingConfig: validatedData.chunkingConfig, }, requestId, { actorUserId: userId } ) logger.info(`[${requestId}] Knowledge base updated: ${id} for user ${userId}`) recordAudit({ workspaceId: accessCheck.knowledgeBase.workspaceId ?? null, actorId: userId, actorName: auth.userName, actorEmail: auth.userEmail, action: AuditAction.KNOWLEDGE_BASE_UPDATED, resourceType: AuditResourceType.KNOWLEDGE_BASE, resourceId: id, resourceName: validatedData.name ?? updatedKnowledgeBase.name, description: `Updated knowledge base "${validatedData.name ?? updatedKnowledgeBase.name}"`, metadata: { updatedFields: Object.keys(validatedData).filter( (k) => validatedData[k as keyof typeof validatedData] !== undefined ), ...(validatedData.name && { newName: validatedData.name }), ...(validatedData.description !== undefined && { description: validatedData.description, }), ...(validatedData.chunkingConfig && { chunkMaxSize: validatedData.chunkingConfig.maxSize, chunkMinSize: validatedData.chunkingConfig.minSize, chunkOverlap: validatedData.chunkingConfig.overlap, }), }, request: req, }) return NextResponse.json({ success: true, data: updatedKnowledgeBase, }) } catch (error) { if (error instanceof KnowledgeBaseConflictError) { return NextResponse.json({ error: error.message }, { status: 409 }) } if (error instanceof KnowledgeBasePermissionError) { logger.warn(`[${requestId}] Forbidden knowledge base update on ${id}: ${error.message}`) return NextResponse.json({ error: error.message }, { status: 403 }) } logger.error(`[${requestId}] Error updating knowledge base`, error) return NextResponse.json({ error: 'Failed to update knowledge base' }, { status: 500 }) } } ) export const DELETE = withRouteHandler( async (_request: NextRequest, { params }: { params: Promise<{ id: string }> }) => { const requestId = generateRequestId() const { id } = await params try { const auth = await checkSessionOrInternalAuth(_request, { requireWorkflowId: false }) if (!auth.success || !auth.userId) { logger.warn(`[${requestId}] Unauthorized knowledge base delete attempt`) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = auth.userId const accessCheck = await checkKnowledgeBaseWriteAccess(id, userId) if (!accessCheck.hasAccess) { if ('notFound' in accessCheck && accessCheck.notFound) { logger.warn(`[${requestId}] Knowledge base not found: ${id}`) return NextResponse.json({ error: 'Knowledge base not found' }, { status: 404 }) } logger.warn( `[${requestId}] User ${userId} attempted to delete unauthorized knowledge base ${id}` ) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } await deleteKnowledgeBase(id, requestId) try { PlatformEvents.knowledgeBaseDeleted({ knowledgeBaseId: id, }) } catch { // Telemetry should not fail the operation } logger.info(`[${requestId}] Knowledge base deleted: ${id} for user ${userId}`) recordAudit({ workspaceId: accessCheck.knowledgeBase.workspaceId ?? null, actorId: userId, actorName: auth.userName, actorEmail: auth.userEmail, action: AuditAction.KNOWLEDGE_BASE_DELETED, resourceType: AuditResourceType.KNOWLEDGE_BASE, resourceId: id, resourceName: accessCheck.knowledgeBase.name, description: `Deleted knowledge base "${accessCheck.knowledgeBase.name || id}"`, metadata: { knowledgeBaseName: accessCheck.knowledgeBase.name, }, request: _request, }) return NextResponse.json({ success: true, data: { message: 'Knowledge base deleted successfully' }, }) } catch (error) { logger.error(`[${requestId}] Error deleting knowledge base`, error) return NextResponse.json({ error: 'Failed to delete knowledge base' }, { status: 500 }) } } )