route.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { NextRequest } from 'next/server';
  2. import { parsePDF } from '@/lib/pdf/pdf-providers';
  3. import { resolvePDFApiKey, resolvePDFBaseUrl } from '@/lib/server/provider-config';
  4. import type { PDFProviderId } from '@/lib/pdf/types';
  5. import type { ParsedPdfContent } from '@/lib/types/pdf';
  6. import { createLogger } from '@/lib/logger';
  7. import { apiError, apiSuccess } from '@/lib/server/api-response';
  8. import { validateUrlForSSRF } from '@/lib/server/ssrf-guard';
  9. const log = createLogger('Parse PDF');
  10. export async function POST(req: NextRequest) {
  11. let pdfFileName: string | undefined;
  12. let resolvedProviderId: string | undefined;
  13. try {
  14. const contentType = req.headers.get('content-type') || '';
  15. if (!contentType.includes('multipart/form-data')) {
  16. log.error('Invalid Content-Type for PDF upload:', contentType);
  17. return apiError(
  18. 'INVALID_REQUEST',
  19. 400,
  20. `Invalid Content-Type: expected multipart/form-data, got "${contentType}"`,
  21. );
  22. }
  23. const formData = await req.formData();
  24. const pdfFile = formData.get('pdf') as File | null;
  25. const providerId = formData.get('providerId') as PDFProviderId | null;
  26. const apiKey = formData.get('apiKey') as string | null;
  27. const baseUrl = formData.get('baseUrl') as string | null;
  28. if (!pdfFile) {
  29. return apiError('MISSING_REQUIRED_FIELD', 400, 'No PDF file provided');
  30. }
  31. // providerId is required from the client — no server-side store to fall back to
  32. const effectiveProviderId = providerId || ('unpdf' as PDFProviderId);
  33. pdfFileName = pdfFile?.name;
  34. resolvedProviderId = effectiveProviderId;
  35. const clientBaseUrl = baseUrl || undefined;
  36. if (clientBaseUrl && process.env.NODE_ENV === 'production') {
  37. const ssrfError = await validateUrlForSSRF(clientBaseUrl);
  38. if (ssrfError) {
  39. return apiError('INVALID_URL', 403, ssrfError);
  40. }
  41. }
  42. const config = {
  43. providerId: effectiveProviderId,
  44. apiKey: clientBaseUrl
  45. ? apiKey || ''
  46. : resolvePDFApiKey(effectiveProviderId, apiKey || undefined),
  47. baseUrl: clientBaseUrl
  48. ? clientBaseUrl
  49. : resolvePDFBaseUrl(effectiveProviderId, baseUrl || undefined),
  50. };
  51. // Convert PDF to buffer
  52. const arrayBuffer = await pdfFile.arrayBuffer();
  53. const buffer = Buffer.from(arrayBuffer);
  54. // Parse PDF using the provider system
  55. const result = await parsePDF(config, buffer);
  56. // Add file metadata
  57. const resultWithMetadata: ParsedPdfContent = {
  58. ...result,
  59. metadata: {
  60. ...result.metadata,
  61. pageCount: result.metadata?.pageCount ?? 0, // Ensure pageCount is always a number
  62. fileName: pdfFile.name,
  63. fileSize: pdfFile.size,
  64. },
  65. };
  66. return apiSuccess({ data: resultWithMetadata });
  67. } catch (error) {
  68. log.error(
  69. `PDF parsing failed [provider=${resolvedProviderId ?? 'unknown'}, file="${pdfFileName ?? 'unknown'}"]:`,
  70. error,
  71. );
  72. return apiError('PARSE_FAILED', 500, error instanceof Error ? error.message : 'Unknown error');
  73. }
  74. }