feat: add backend OCR manual proxy endpoint (refs #135)
Add POST /api/ocr/extract/manual endpoint that proxies to the Python OCR service's manual extraction pipeline. Includes Pro tier gating via document.scanMaintenanceSchedule, PDF-only validation, 200MB file size limit, and async 202 job response for polling via existing job status endpoint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
57
backend/src/features/ocr/external/ocr-client.ts
vendored
57
backend/src/features/ocr/external/ocr-client.ts
vendored
@@ -2,7 +2,7 @@
|
||||
* @ai-summary HTTP client for OCR service communication
|
||||
*/
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
import type { JobResponse, OcrResponse, ReceiptExtractionResponse, VinExtractionResponse } from '../domain/ocr.types';
|
||||
import type { JobResponse, ManualJobResponse, OcrResponse, ReceiptExtractionResponse, VinExtractionResponse } from '../domain/ocr.types';
|
||||
|
||||
/** OCR service configuration */
|
||||
const OCR_SERVICE_URL = process.env.OCR_SERVICE_URL || 'http://mvp-ocr:8000';
|
||||
@@ -265,6 +265,61 @@ export class OcrClient {
|
||||
return (await response.json()) as JobResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit an async manual extraction job for PDF owner's manuals.
|
||||
*
|
||||
* @param fileBuffer - PDF file buffer
|
||||
* @param contentType - MIME type of the file (must be application/pdf)
|
||||
* @param vehicleId - Optional vehicle ID for context
|
||||
* @returns Manual job submission response
|
||||
*/
|
||||
async submitManualJob(
|
||||
fileBuffer: Buffer,
|
||||
contentType: string,
|
||||
vehicleId?: string
|
||||
): Promise<ManualJobResponse> {
|
||||
const formData = this.buildFormData(fileBuffer, contentType);
|
||||
if (vehicleId) {
|
||||
formData.append('vehicle_id', vehicleId);
|
||||
}
|
||||
|
||||
const url = `${this.baseUrl}/extract/manual`;
|
||||
|
||||
logger.info('OCR manual job submit request', {
|
||||
operation: 'ocr.client.submitManualJob',
|
||||
url,
|
||||
contentType,
|
||||
fileSize: fileBuffer.length,
|
||||
hasVehicleId: !!vehicleId,
|
||||
});
|
||||
|
||||
const response = await this.fetchWithTimeout(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
logger.error('OCR manual job submit failed', {
|
||||
operation: 'ocr.client.submitManualJob.error',
|
||||
status: response.status,
|
||||
error: errorText,
|
||||
});
|
||||
throw new Error(`OCR service error: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const result = (await response.json()) as ManualJobResponse;
|
||||
|
||||
logger.info('OCR manual job submitted', {
|
||||
operation: 'ocr.client.submitManualJob.success',
|
||||
jobId: result.jobId,
|
||||
status: result.status,
|
||||
estimatedSeconds: result.estimatedSeconds,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OCR service is healthy.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user