feat(brandkit): permitir subir PDF y DOCX como fuentes

Extrae texto del PDF/DOCX en Fuentes del kit de marca para no depender
de pegar el contenido a mano.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-16 13:26:06 +02:00
parent 77fd628078
commit 5c76ae18bb
3 changed files with 309 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
/**
* POST multipart: file (.txt/.md/.csv) or JSON { text, filename }
* POST multipart: file (.txt/.md/.csv/.json/.pdf/.docx) or JSON { text, filename }
* Appends source excerpt into the tenant brandbook.
*/
import { NextRequest, NextResponse } from 'next/server';
@@ -15,8 +15,13 @@ import {
parseBrandbook,
type Brandbook,
} from '@/lib/brandbook';
import {
detectSourceExt,
extractSourceText,
} from '@/lib/extract-document-text';
export const dynamic = 'force-dynamic';
export const maxDuration = 60;
async function loadBook(tenantId: string, label: string): Promise<{
id: string | null;
@@ -68,22 +73,42 @@ export async function POST(req: NextRequest) {
} else if (!url && file && typeof file === 'object' && 'arrayBuffer' in file) {
const f = file as File;
filename = f.name || 'upload.txt';
const lower = filename.toLowerCase();
if (
!lower.endsWith('.txt') &&
!lower.endsWith('.md') &&
!lower.endsWith('.csv') &&
!lower.endsWith('.json')
) {
const ext = detectSourceExt(filename, f.type);
if (!ext) {
return NextResponse.json(
{
error:
'Por ahora sube .txt, .md, .csv o .json. PDF/DOCX: pega el texto en el campo.',
'Formatos admitidos: .pdf, .docx, .txt, .md, .csv, .json. Si el PDF es escaneado, pega el texto.',
},
{ status: 400 }
);
}
try {
const buf = Buffer.from(await f.arrayBuffer());
if (buf.byteLength > 25 * 1024 * 1024) {
return NextResponse.json(
{ error: 'Archivo demasiado grande (máx. 25 MB)' },
{ status: 400 }
);
}
const extracted = await extractSourceText({
filename,
mime: f.type,
buffer: buf,
});
text = extracted.text;
filename = extracted.filename;
} catch (e) {
return NextResponse.json(
{
error:
e instanceof Error
? e.message
: 'No se pudo leer el archivo',
},
{ status: 400 }
);
}
text = await f.text();
}
} else {
const body = (await req.json().catch(() => ({}))) as {