setUserAgent($userAgent); } $ua = $detect->getUserAgent(); // === DETECT BRAND === $brand = 'Unknown'; // CEK BRAND SPESIFIK DULU if (preg_match('/POCO/i', $ua)) { $brand = 'POCO'; } elseif (preg_match('/Redmi/i', $ua)) { $brand = 'Redmi'; } elseif (preg_match('/Infinix/i', $ua)) { $brand = 'Infinix'; } elseif (preg_match('/Samsung|SM-|GT-|SCH-/i', $ua)) { $brand = 'Samsung'; } elseif (preg_match('/OPPO|CPH/i', $ua)) { $brand = 'Oppo'; } elseif (preg_match('/vivo|V\d{4}/i', $ua)) { $brand = 'Vivo'; } elseif (preg_match('/Realme|RMX/i', $ua)) { $brand = 'Realme'; } else { $androidBrands = [ 'Xiaomi', 'Huawei', 'OnePlus', 'Asus', 'Nokia', 'Sony', 'Motorola', 'LG', 'Lenovo', 'Tecno', 'Google', 'HTC', 'Meizu', 'ZTE' ]; foreach ($androidBrands as $b) { if ($detect->is($b)) { $brand = $b; break; } } } // Detect iPhone & iPad if ($detect->isiOS() && !$detect->isTablet()) { $brand = 'iPhone'; } elseif ($detect->isiOS() && $detect->isTablet()) { $brand = 'iPad'; } // === DETECT MODEL === $model = 'Unknown'; // Samsung models if ($brand === 'Samsung') { if (preg_match('/(SM-[A-Z]\d{3,4}[A-Z]?|GT-[A-Z0-9]+|SCH-[A-Z0-9]+)/i', $ua, $matches)) { $model = $matches[1]; } } // ⭐ FIX: POCO models - bisa berupa nama atau kode angka elseif ($brand === 'POCO') { // Cari pattern: POCO [nama model] atau kode angka (contoh: 21121210G) if (preg_match('/POCO\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'POCO ' . trim($matches[1]); } // Jika tidak ada nama POCO, cari kode angka Xiaomi elseif (preg_match('/\s([0-9]{8,9}[A-Z]?)(?:\s+Build)/i', $ua, $matches)) { $model = $matches[1] . ' (POCO)'; } } // Redmi models elseif ($brand === 'Redmi') { if (preg_match('/Redmi\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'Redmi ' . trim($matches[1]); } // Fallback: kode angka elseif (preg_match('/\s([0-9]{8,9}[A-Z]?)(?:\s+Build)/i', $ua, $matches)) { $model = $matches[1] . ' (Redmi)'; } } // Xiaomi models elseif ($brand === 'Xiaomi') { if (preg_match('/Mi\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'Mi ' . trim($matches[1]); } // Fallback: kode angka elseif (preg_match('/\s([0-9]{8,9}[A-Z]?)(?:\s+Build)/i', $ua, $matches)) { $model = $matches[1] . ' (Xiaomi)'; } } // Infinix models elseif ($brand === 'Infinix') { if (preg_match('/Infinix\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'Infinix ' . trim($matches[1]); } elseif (preg_match('/(X\d{3,4}[A-Z]?)/i', $ua, $matches)) { $model = $matches[1]; } } // Oppo models elseif ($brand === 'Oppo') { if (preg_match('/(CPH\d+|PBAM\d+|PBEM\d+)/i', $ua, $matches)) { $model = $matches[1]; } elseif (preg_match('/OPPO\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'OPPO ' . trim($matches[1]); } } // Vivo models elseif ($brand === 'Vivo') { if (preg_match('/(V\d{4})/i', $ua, $matches)) { $model = $matches[1]; } elseif (preg_match('/vivo\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'vivo ' . trim($matches[1]); } } // Realme models elseif ($brand === 'Realme') { if (preg_match('/(RMX\d+)/i', $ua, $matches)) { $model = $matches[1]; } elseif (preg_match('/Realme\s+([A-Z0-9\s\-]+?)(?:\s+Build|\)|;)/i', $ua, $matches)) { $model = 'Realme ' . trim($matches[1]); } } // iPhone models elseif ($brand === 'iPhone') { if (preg_match('/iPhone(\d+[,\d]*)?/i', $ua, $matches)) { $model = 'iPhone' . ($matches[1] ? ' ' . str_replace(',', '.', $matches[1]) : ''); } else { $model = 'iPhone'; } } // iPad models elseif ($brand === 'iPad') { if (preg_match('/iPad[\d,]*;/i', $ua, $matches)) { $model = trim(str_replace(';', '', $matches[0])); } } // === DETECT OS & VERSION === $os = 'Unknown'; $osVersion = 'Unknown'; if ($detect->isiOS()) { $os = 'iOS'; $osVersion = $detect->version('iOS') ?: 'Unknown'; } elseif ($detect->isAndroidOS()) { $os = 'Android'; $osVersion = $detect->version('Android') ?: 'Unknown'; } elseif (preg_match('/Windows/i', $ua)) { $os = 'Windows'; if (preg_match('/Windows NT ([0-9\.]+)/i', $ua, $matches)) { $osVersion = $matches[1]; } } // === DETECT BROWSER === $browser = 'Unknown'; $browserVersion = 'Unknown'; // Chrome if (preg_match('/Chrome\/([0-9\.]+)/i', $ua, $matches)) { $browser = 'Chrome'; $browserVersion = $matches[1]; } // Safari (non-Chrome) elseif (preg_match('/Safari\/([0-9\.]+)/i', $ua, $matches) && !preg_match('/Chrome/i', $ua)) { $browser = 'Safari'; if (preg_match('/Version\/([0-9\.]+)/i', $ua, $vMatches)) { $browserVersion = $vMatches[1]; } } // Firefox elseif (preg_match('/Firefox\/([0-9\.]+)/i', $ua, $matches)) { $browser = 'Firefox'; $browserVersion = $matches[1]; } // === DEVICE TYPE === $deviceType = 'desktop'; if ($detect->isTablet()) { $deviceType = 'tablet'; } elseif ($detect->isMobile()) { $deviceType = 'phone'; } return [ 'brand' => $brand, 'model' => $model, 'os' => $os, 'os_version' => $osVersion, 'browser' => $browser, 'browser_version' => $browserVersion, 'device_type' => $deviceType, 'is_mobile' => $detect->isMobile(), 'is_tablet' => $detect->isTablet(), 'is_phone' => $detect->isMobile() && !$detect->isTablet(), 'user_agent' => $ua ]; } } if (!function_exists('getCameraVersionSupport')) { /** * Get camera version support untuk device tertentu * * @param string $deviceBrand Brand device (Samsung, POCO, dll) * @param string $deviceModel Model device (SM-S931B, POCO X5, dll) * @return array Camera version support info */ function getCameraVersionSupport($deviceBrand, $deviceModel) { $db = \Config\Database::connect(); // Cek apakah ada mapping SPESIFIK untuk model ini $compatibility = $db->table('device_camera_compatibility') ->where('device_brand', $deviceBrand) ->where('device_model', $deviceModel) ->where('is_active', true) ->get() ->getRow(); // Jika tidak ada, cek mapping untuk BRAND (device_model = NULL) if (!$compatibility) { $compatibility = $db->table('device_camera_compatibility') ->where('device_brand', $deviceBrand) ->where('device_model', null) ->where('is_active', true) ->get() ->getRow(); } // JIKA TIDAK ADA DI DATABASE = SUPPORT SEMUA VERSI (DEFAULT) if (!$compatibility) { return [ 'support_version_1' => true, 'support_version_2' => true, 'support_version_3' => true, 'recommended_version' => 3, // Recommend versi tertinggi 'available_versions' => [1, 2, 3], 'notes' => 'Device tidak ada limitasi - support semua versi kamera', 'is_default' => true // Flag untuk tahu ini default behavior ]; } // Build available versions array $availableVersions = []; if ($compatibility->support_version_1) $availableVersions[] = 1; if ($compatibility->support_version_2) $availableVersions[] = 2; if ($compatibility->support_version_3) $availableVersions[] = 3; return [ 'support_version_1' => (bool)$compatibility->support_version_1, 'support_version_2' => (bool)$compatibility->support_version_2, 'support_version_3' => (bool)$compatibility->support_version_3, 'recommended_version' => $compatibility->recommended_version, 'available_versions' => $availableVersions, 'notes' => $compatibility->notes, 'is_default' => false // Ini bukan default, ada di database ]; } } if (!function_exists('getDeviceFingerprint')) { function getDeviceFingerprint($userAgent, $ipAddress) { return md5($userAgent . '|' . $ipAddress); } } if (!function_exists('isDeviceTrusted')) { /** * Cek apakah device sudah dipercaya * * @param string $deviceFingerprint * @param string $superUserId * @return bool */ function isDeviceTrusted($deviceFingerprint, $superUserId) { $db = \Config\Database::connect(); $device = $db->table('user_devices') ->where('device_fingerprint', $deviceFingerprint) ->where('super_user_id', $superUserId) ->where('is_trusted', true) ->where('is_blocked', false) ->get() ->getRow(); return $device !== null; } } if (!function_exists('isDeviceBlocked')) { /** * Cek apakah device diblokir * * @param string $deviceFingerprint * @return array|null Device info if blocked, null otherwise */ function isDeviceBlocked($deviceFingerprint) { $db = \Config\Database::connect(); $device = $db->table('user_devices') ->where('device_fingerprint', $deviceFingerprint) ->where('is_blocked', true) ->get() ->getRow(); if ($device) { return [ 'is_blocked' => true, 'reason' => $device->blocked_reason, 'blocked_at' => $device->blocked_at ]; } return null; } }