payfrit-api/api/menu/uploadHeader.php
John Mizerek 4a4a098551 Fix upload paths to use Lucee webroot and accept uppercase OTP keys
Upload endpoints were saving files to PHP's DOCUMENT_ROOT instead of
the Lucee webroot where the Android app loads them from. Also fix
verifyLoginOTP and verifyOTP to accept both UUID/OTP and uuid/otp keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:04:27 -07:00

108 lines
3.4 KiB
PHP

<?php
require_once __DIR__ . '/../helpers.php';
runAuth();
/**
* Upload Business Header Image
*
* Multipart form: BusinessID (int), header (file)
* Or X-Business-ID header for BusinessID.
*/
// Get BusinessID from form, then header
$bizId = (int) ($_POST['BusinessID'] ?? 0);
if ($bizId <= 0) {
$bizId = (int) headerValue('X-Business-ID');
}
if ($bizId <= 0) {
apiAbort(['OK' => false, 'ERROR' => 'missing_businessid', 'MESSAGE' => 'BusinessID is required']);
}
if (!isset($_FILES['header']) || $_FILES['header']['error'] !== UPLOAD_ERR_OK) {
jsonResponse(['OK' => false, 'ERROR' => 'no_file', 'MESSAGE' => 'No file was uploaded']);
}
$webroot = isDev()
? '/opt/lucee/tomcat/webapps/ROOT'
: '/var/www/biz.payfrit.com';
$headersDir = $webroot . '/uploads/headers';
if (!is_dir($headersDir)) {
mkdir($headersDir, 0755, true);
}
try {
$tmpFile = $_FILES['header']['tmp_name'];
// Detect actual format from file contents (magic bytes)
$actualExt = '';
$fh = fopen($tmpFile, 'rb');
$header = fread($fh, 8);
fclose($fh);
$hex = strtoupper(bin2hex($header));
if (str_starts_with($hex, 'FFD8')) {
$actualExt = 'jpg';
} elseif (str_starts_with($hex, '89504E470D0A1A0A')) {
$actualExt = 'png';
} elseif (str_starts_with($hex, '474946')) {
$actualExt = 'gif';
} elseif (str_starts_with($hex, '52494646')) {
// RIFF container — could be WEBP
$actualExt = 'webp';
}
// Fallback to client extension
if ($actualExt === '') {
$actualExt = strtolower(pathinfo($_FILES['header']['name'], PATHINFO_EXTENSION));
}
$allowed = ['jpg', 'jpeg', 'gif', 'png', 'webp', 'heic', 'heif'];
if (!in_array($actualExt, $allowed)) {
jsonResponse(['OK' => false, 'ERROR' => 'invalid_type', 'MESSAGE' => 'Only image files are accepted (jpg, jpeg, gif, png, webp, heic)']);
}
// Convert HEIC/HEIF extension to jpg for consistency
if ($actualExt === 'heic' || $actualExt === 'heif') {
$actualExt = 'jpg';
}
// Delete old header if exists
$old = queryOne("SELECT HeaderImageExtension FROM Businesses WHERE ID = ?", [$bizId]);
if ($old && !empty($old['HeaderImageExtension'])) {
$oldFile = "$headersDir/{$bizId}.{$old['HeaderImageExtension']}";
if (file_exists($oldFile)) {
@unlink($oldFile);
}
}
// Delete destination file if it already exists (same extension re-upload)
$destFile = "$headersDir/{$bizId}.{$actualExt}";
if (file_exists($destFile)) {
@unlink($destFile);
}
// Move uploaded file
if (!move_uploaded_file($tmpFile, $destFile)) {
jsonResponse(['OK' => false, 'ERROR' => 'upload_failed', 'MESSAGE' => 'Failed to save uploaded file']);
}
// Update database
queryTimed("UPDATE Businesses SET HeaderImageExtension = ? WHERE ID = ?", [$actualExt, $bizId]);
// Get image dimensions
$imgSize = @getimagesize($destFile);
$width = $imgSize[0] ?? 0;
$height = $imgSize[1] ?? 0;
jsonResponse([
'OK' => true,
'ERROR' => '',
'MESSAGE' => 'Header uploaded successfully',
'HEADERURL' => "/uploads/headers/{$bizId}.{$actualExt}",
'WIDTH' => $width,
'HEIGHT' => $height,
]);
} catch (Exception $e) {
jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => $e->getMessage(), 'DETAIL' => '']);
}