fix: OCR API error
All checks were successful
Deploy to Staging / Build Images (push) Successful in 7m45s
Deploy to Staging / Deploy to Staging (push) Successful in 51s
Deploy to Staging / Verify Staging (push) Successful in 2m31s
Deploy to Staging / Notify Staging Ready (push) Successful in 8s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
All checks were successful
Deploy to Staging / Build Images (push) Successful in 7m45s
Deploy to Staging / Deploy to Staging (push) Successful in 51s
Deploy to Staging / Verify Staging (push) Successful in 2m31s
Deploy to Staging / Notify Staging Ready (push) Successful in 8s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
This commit is contained in:
79
backend/src/features/ocr/external/ocr-client.ts
vendored
79
backend/src/features/ocr/external/ocr-client.ts
vendored
@@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @ai-summary HTTP client for OCR service communication
|
||||
*/
|
||||
import FormData from 'form-data';
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
import type { JobResponse, OcrResponse } from '../domain/ocr.types';
|
||||
import type { JobResponse, OcrResponse, VinExtractionResponse } from '../domain/ocr.types';
|
||||
|
||||
/** OCR service configuration */
|
||||
const OCR_SERVICE_URL = process.env.OCR_SERVICE_URL || 'http://mvp-ocr:8000';
|
||||
@@ -32,12 +31,7 @@ export class OcrClient {
|
||||
contentType: string,
|
||||
preprocess: boolean = true
|
||||
): Promise<OcrResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', fileBuffer, {
|
||||
filename: this.getFilenameFromContentType(contentType),
|
||||
contentType,
|
||||
});
|
||||
|
||||
const formData = this.buildFormData(fileBuffer, contentType);
|
||||
const url = `${this.baseUrl}/extract?preprocess=${preprocess}`;
|
||||
|
||||
logger.info('OCR extract request', {
|
||||
@@ -50,8 +44,7 @@ export class OcrClient {
|
||||
|
||||
const response = await this.fetchWithTimeout(url, {
|
||||
method: 'POST',
|
||||
body: formData as any,
|
||||
headers: formData.getHeaders(),
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -77,6 +70,55 @@ export class OcrClient {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract VIN from an image using VIN-specific OCR.
|
||||
*
|
||||
* @param fileBuffer - Image file buffer
|
||||
* @param contentType - MIME type of the file
|
||||
* @returns VIN extraction result
|
||||
*/
|
||||
async extractVin(
|
||||
fileBuffer: Buffer,
|
||||
contentType: string
|
||||
): Promise<VinExtractionResponse> {
|
||||
const formData = this.buildFormData(fileBuffer, contentType);
|
||||
const url = `${this.baseUrl}/extract/vin`;
|
||||
|
||||
logger.info('OCR VIN extract request', {
|
||||
operation: 'ocr.client.extractVin',
|
||||
url,
|
||||
contentType,
|
||||
fileSize: fileBuffer.length,
|
||||
});
|
||||
|
||||
const response = await this.fetchWithTimeout(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
logger.error('OCR VIN extract failed', {
|
||||
operation: 'ocr.client.extractVin.error',
|
||||
status: response.status,
|
||||
error: errorText,
|
||||
});
|
||||
throw new Error(`OCR service error: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const result = (await response.json()) as VinExtractionResponse;
|
||||
|
||||
logger.info('OCR VIN extract completed', {
|
||||
operation: 'ocr.client.extractVin.success',
|
||||
success: result.success,
|
||||
vin: result.vin,
|
||||
confidence: result.confidence,
|
||||
processingTimeMs: result.processingTimeMs,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit an async OCR job for large files.
|
||||
*
|
||||
@@ -90,11 +132,7 @@ export class OcrClient {
|
||||
contentType: string,
|
||||
callbackUrl?: string
|
||||
): Promise<JobResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', fileBuffer, {
|
||||
filename: this.getFilenameFromContentType(contentType),
|
||||
contentType,
|
||||
});
|
||||
const formData = this.buildFormData(fileBuffer, contentType);
|
||||
if (callbackUrl) {
|
||||
formData.append('callback_url', callbackUrl);
|
||||
}
|
||||
@@ -111,8 +149,7 @@ export class OcrClient {
|
||||
|
||||
const response = await this.fetchWithTimeout(url, {
|
||||
method: 'POST',
|
||||
body: formData as any,
|
||||
headers: formData.getHeaders(),
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -205,6 +242,14 @@ export class OcrClient {
|
||||
}
|
||||
}
|
||||
|
||||
private buildFormData(fileBuffer: Buffer, contentType: string): FormData {
|
||||
const filename = this.getFilenameFromContentType(contentType);
|
||||
const blob = new Blob([fileBuffer], { type: contentType });
|
||||
const formData = new FormData();
|
||||
formData.append('file', blob, filename);
|
||||
return formData;
|
||||
}
|
||||
|
||||
private getFilenameFromContentType(contentType: string): string {
|
||||
const extensions: Record<string, string> = {
|
||||
'image/jpeg': 'image.jpg',
|
||||
|
||||
Reference in New Issue
Block a user