fix(knowledge): address review comments on upsert document

- Reorder create-then-delete to prevent data loss if creation fails
- Move Zod validation before workflow authorization for validated input
- Fix btoa stack overflow for large content using loop-based encoding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed Latif
2026-03-17 17:18:03 -07:00
parent 8bf63fe667
commit 3e4a6e451a
2 changed files with 21 additions and 12 deletions

View File

@@ -55,9 +55,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
const userId = auth.userId
if (body.workflowId) {
const validatedData = UpsertDocumentSchema.parse(body)
if (validatedData.workflowId) {
const authorization = await authorizeWorkflowByWorkspacePermission({
workflowId: body.workflowId,
workflowId: validatedData.workflowId,
userId,
action: 'write',
})
@@ -82,8 +84,6 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const validatedData = UpsertDocumentSchema.parse(body)
let existingDocumentId: string | null = null
let isUpdate = false
@@ -126,9 +126,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
if (existingDocumentId) {
isUpdate = true
logger.info(
`[${requestId}] Found existing document ${existingDocumentId}, deleting before re-creation`
`[${requestId}] Found existing document ${existingDocumentId}, creating replacement before deleting old`
)
await deleteDocument(existingDocumentId, requestId)
}
const createdDocuments = await createDocumentRecords(
@@ -147,6 +146,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
requestId
)
if (existingDocumentId) {
await deleteDocument(existingDocumentId, requestId)
}
const firstDocument = createdDocuments[0]
processDocumentsWithQueue(

View File

@@ -81,12 +81,18 @@ export const knowledgeUpsertDocumentTool: ToolConfig<
throw new Error('Document content exceeds maximum size of 1MB')
}
const contentBytes = new TextEncoder().encode(textContent).length
const base64Content =
typeof Buffer !== 'undefined'
? Buffer.from(textContent, 'utf8').toString('base64')
: btoa(String.fromCharCode(...new TextEncoder().encode(textContent)))
const utf8Bytes = new TextEncoder().encode(textContent)
const contentBytes = utf8Bytes.length
let base64Content: string
if (typeof Buffer !== 'undefined') {
base64Content = Buffer.from(textContent, 'utf8').toString('base64')
} else {
let binary = ''
for (let i = 0; i < utf8Bytes.length; i++) {
binary += String.fromCharCode(utf8Bytes[i])
}
base64Content = btoa(binary)
}
const dataUri = `data:text/plain;base64,${base64Content}`