'Unauthenticated']); exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['error' => 'POST required']); exit; } $type = $_POST['type'] ?? 'media'; // media | logo // Allowed types $allowedMime = ['image/jpeg','image/jpg','image/png','image/gif','image/webp']; $maxBytes = 5 * 1024 * 1024; // 5MB $file = $_FILES['file'] ?? null; if (!$file || $file['error'] !== UPLOAD_ERR_OK) { $errMsg = match($file['error'] ?? 99) { UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'File too large.', UPLOAD_ERR_NO_FILE => 'No file selected.', default => 'Upload failed. Try again.' }; echo json_encode(['error' => $errMsg]); exit; } if ($file['size'] > $maxBytes) { echo json_encode(['error' => 'File must be under 5MB.']); exit; } // Verify MIME by reading file header (don't trust $_FILES['type']) $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); if (!in_array($mime, $allowedMime)) { echo json_encode(['error' => 'Only JPG, PNG, GIF, WebP images allowed.']); exit; } // Build safe filename $ext = match($mime) { 'image/jpeg', 'image/jpg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp', default => 'jpg' }; $user = currentUser(); $filename = 'media_' . $user['id'] . '_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext; // Ensure upload directory exists $uploadDir = __DIR__ . '/../uploads/media/'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); // Protect directory file_put_contents($uploadDir . '.htaccess', "\n Deny from all\n\n"); } $destPath = $uploadDir . $filename; if (!move_uploaded_file($file['tmp_name'], $destPath)) { echo json_encode(['error' => 'Failed to save file. Check server permissions.']); exit; } // Return the public URL $publicUrl = APP_URL . '/uploads/media/' . $filename; echo json_encode(['success' => true, 'url' => $publicUrl, 'filename' => $filename]);