Update: 2026-07-30 02:27:45
This commit is contained in:
@@ -84,7 +84,31 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
$extension = pathinfo($_FILES['invoice']['name'], PATHINFO_EXTENSION);
|
||||
// 4a. Validate the file BEFORE storing it. The client-supplied
|
||||
// $_FILES['invoice']['type'] is attacker-controlled, so sniff the real type
|
||||
// off the temp file and derive the extension from that, never from the name.
|
||||
$allowedMimeTypes = [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/webp' => 'webp',
|
||||
'image/heic' => 'heic',
|
||||
'image/heif' => 'heif',
|
||||
'application/pdf' => 'pdf',
|
||||
];
|
||||
|
||||
$maxSize = 10 * 1024 * 1024; // 10MB
|
||||
if ($_FILES['invoice']['size'] > $maxSize) {
|
||||
json_error('حجم الملف أكبر من 10 ميغابايت', 422);
|
||||
exit;
|
||||
}
|
||||
|
||||
$detectedMime = @mime_content_type($_FILES['invoice']['tmp_name']) ?: '';
|
||||
if (!isset($allowedMimeTypes[$detectedMime])) {
|
||||
json_error('نوع الملف غير مدعوم. المسموح: صور (JPG, PNG, WEBP, HEIC) و PDF', 422);
|
||||
exit;
|
||||
}
|
||||
|
||||
$extension = $allowedMimeTypes[$detectedMime];
|
||||
$fileName = bin2hex(random_bytes(8)) . '_' . time() . '.' . $extension;
|
||||
$targetFile = $uploadDir . $fileName;
|
||||
|
||||
@@ -94,13 +118,17 @@ try {
|
||||
}
|
||||
|
||||
// 5. Run AI Extraction
|
||||
$mimeType = $_FILES['invoice']['type'];
|
||||
$mimeType = $detectedMime;
|
||||
$fileContent = file_get_contents($targetFile);
|
||||
$base64Data = base64_encode($fileContent);
|
||||
|
||||
$extracted = AI::extractInvoiceData($base64Data, $mimeType);
|
||||
// extractInvoices() returns EVERY invoice in the image. extractInvoiceData()
|
||||
// silently kept only the first, so a photo holding two receipts lost one.
|
||||
AI::setTenantContext($tenantId);
|
||||
$extractedInvoices = AI::extractInvoices($base64Data, $mimeType);
|
||||
AI::setTenantContext(null);
|
||||
|
||||
if (!$extracted) {
|
||||
if (empty($extractedInvoices)) {
|
||||
$invoiceId = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO invoices (
|
||||
@@ -115,9 +143,14 @@ try {
|
||||
}
|
||||
|
||||
// 6. Save Extracted Data
|
||||
// Multiple invoices in one image each consume a credit, so make sure the
|
||||
// tenant can actually cover the whole set before writing any of them.
|
||||
if ($decoded['role'] !== 'super_admin' && count($extractedInvoices) > 1) {
|
||||
QuotaMiddleware::checkInvoiceQuota($tenantId, count($extractedInvoices));
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
$extractedInvoices = $extracted['invoices'] ?? [$extracted];
|
||||
$savedIds = [];
|
||||
|
||||
foreach ($extractedInvoices as $inv) {
|
||||
|
||||
Reference in New Issue
Block a user