diff --git a/app/Console/Commands/clientAssetGenerator.php b/app/Console/Commands/clientAssetGenerator.php index d0e72b3..5ad3746 100644 --- a/app/Console/Commands/clientAssetGenerator.php +++ b/app/Console/Commands/clientAssetGenerator.php @@ -40,7 +40,7 @@ class clientAssetGenerator extends Command // prepare client.scss and add gfx variable - $js = "// PLEASE DO NOT EDIT THIS FILE, \n// IF YOU WANT ADD ANY CODE CREATE NEW JS INTO client-custom" . PHP_EOL; + $js = "// PLEASE DO NOT EDIT THIS FILE, \n// IF YOU WANT ADD ANY CODE CREATE NEW JS INTO client-custom \n import axios from 'axios'; \n window.axios = axios; \n \n window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';" . PHP_EOL; $variables = "// PLEASE DO NOT EDIT THIS FILE, \n// IF YOU WANT ADD ANY CODE CREATE NEW SCSS INTO client-custom" . PHP_EOL; foreach ($vars as $k => $var) { $variables .= '$'."$k:$var;" . PHP_EOL; diff --git a/app/Helpers/TVisitor.php b/app/Helpers/TVisitor.php new file mode 100644 index 0000000..f2c436d --- /dev/null +++ b/app/Helpers/TVisitor.php @@ -0,0 +1,426 @@ + + * @date : 3-April-2013 (14-1-1392) + * @time : 20:32 + * @subpackage TVisitor + * @version 1.0 + * @todo : TVisitor class for get visitor info + */ +class TVisitor { + + function __construct() { + + } + + /** + * @todo Detect visitor OS + * @return int os number + */ + public static function DetectOSI() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) + return 0; + + $os_list = array( + '(Linux)', + '(Windows NT 11.0)', // Added Windows 11 + '(Windows NT 10.0)', + '(Windows NT 6.3)', + '(Windows NT 6.2)', + '(Windows NT 6.1)', + '(Windows NT 6.0)', + '(Windows NT 5.2)', + '(Windows NT 5.1)', + '(Windows NT 5.0)', + '(Windows NT 4.0)', + '(Win 9x 4.90)', + '(Windows 98)', + '(Windows 95)', + '(Windows CE)', + 'Windows (iPhone|iPad)', + '(iPhone)|(iPad)', + '(Mac OS X)', + '(MacPPC)|(Mac_PowerPC)|(Macintosh)', + '(Ubuntu)', + '(Linux Mint)', + '(Debian)', + '(Fedora)', + '(Red Hat)', + '(SuSE)', + '(Android)', + '(webOS)|(hpwOS)', + '(BlackBerry)', + '(Symbian)', + '(FreeBSD)', + '(OpenBSD)', + '(NetBSD)', + '(SunOS)', + '(OpenSolaris)', + '(Chrome OS)', + '(CrOS)', + '(bot)' + ); + + foreach ($os_list as $index => $match) { + if (preg_match("/$match/i", $_SERVER['HTTP_USER_AGENT'])) { + return $index + 1; + } + } + + return null; + } + + /** + * @todo Detect visitor OS + * @return string OS name + */ + public static function DetectOS() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) + return 'Unknown'; + + $os_list = array( + 'Linux' => '(Linux)', + 'Windows 11' => '(Windows NT 11.0)', // Added Windows 11 + 'Windows 10' => '(Windows NT 10.0)', + 'Windows 8.1' => '(Windows NT 6.3)', + 'Windows 8' => '(Windows NT 6.2)', + 'Windows 7' => '(Windows NT 6.1)', + 'Windows Vista' => '(Windows NT 6.0)', + 'Windows Server 2003/XP x64' => '(Windows NT 5.2)', + 'Windows XP' => '(Windows NT 5.1)', + 'Windows 2000' => '(Windows NT 5.0)', + 'Windows ME' => '(Win 9x 4.90)', + 'Windows 98' => '(Windows 98)', + 'Windows 95' => '(Windows 95)', + 'Windows CE' => '(Windows CE)', + 'Windows (iPhone/iPad)' => 'Windows (iPhone|iPad)', + 'iPhone/iPad' => '(iPhone)|(iPad)', + 'Mac OS X' => '(Mac OS X)', + 'Mac OS' => '(MacPPC)|(Mac_PowerPC)|(Macintosh)', + 'Ubuntu' => '(Ubuntu)', + 'Linux Mint' => '(Linux Mint)', + 'Debian' => '(Debian)', + 'Fedora' => '(Fedora)', + 'Red Hat' => '(Red Hat)', + 'SuSE' => '(SuSE)', + 'Android' => '(Android)', + 'webOS' => '(webOS)|(hpwOS)', + 'BlackBerry' => '(BlackBerry)', + 'Symbian' => '(Symbian)', + 'FreeBSD' => '(FreeBSD)', + 'OpenBSD' => '(OpenBSD)', + 'NetBSD' => '(NetBSD)', + 'SunOS' => '(SunOS)', + 'OpenSolaris' => '(OpenSolaris)', + 'Chrome OS' => '(Chrome OS)|(CrOS)', + 'bot' => '(bot)' + ); + + foreach ($os_list as $os => $pattern) { + if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) { + return $os; + } + } + + return 'Unknown'; + } + + /** + * @todo Detect if visitor is using a mobile device + * @return bool + */ + public static function IsMobile() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + + // List of mobile devices and operating systems + $mobile_agents = [ + 'mobile', 'android', 'iphone', 'ipod', 'ipad', 'windows phone', 'blackberry', 'kindle', 'silk', + 'opera mini', 'opera mobi', 'webos', 'symbian', 'nokia', 'samsung', 'lg', 'htc', 'mot', 'tablet', + 'rim tablet', 'meego', 'netfront', 'bolt', 'fennec', 'series60', 'maemo', 'midp', 'cldc', 'up.browser', + 'up.link', 'mmp', 'symbian', 'smartphone', 'wap' + ]; + + // Check if user agent contains any mobile keywords + foreach ($mobile_agents as $agent) { + if (strpos($user_agent, $agent) !== false) { + return true; + } + } + + // Check for mobile-specific headers + if (isset($_SERVER['HTTP_ACCEPT'])) { + if (strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') !== false) { + return true; + } + } + + if (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) { + return true; + } + + // Check for Opera Mini + if (isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])) { + return true; + } + + // Use PHP's built-in mobile detection (if available) + if (function_exists('http_negotiate_language')) { + $accept = http_negotiate_language(['wap', 'html']); + if ($accept === 'wap') { + return true; + } + } + + return false; + } + + + + /** + * @todo Get browser name only + * @return string browser name + */ + public static function DetectBrowser() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) + return 'Unknown'; + + $browser_list = array( + 'Firefox' => '(Firefox)', + 'Edge' => '(Edg|Edge)', + 'Chrome' => '(Chrome)(?!.*Edge)', + 'Safari' => '(Safari)(?!.*Chrome)', + 'Opera' => '(OPR|Opera)', + 'Brave' => '(Brave)', + 'Internet Explorer' => '(MSIE|Trident)', + 'DeepNet Explorer' => '(Deepnet)', + 'Flock' => '(Flock)', + 'Maxthon' => '(Maxthon)', + 'Avant Browser' => '(Avant)', + 'AOL' => '(AOL)', + 'Vivaldi' => '(Vivaldi)', + 'UC Browser' => '(UCBrowser)', + 'Yandex Browser' => '(YaBrowser)', + 'Samsung Internet' => '(SamsungBrowser)', + ); + + foreach ($browser_list as $browser => $pattern) { + if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) { + return $browser; + } + } + + return 'Other'; + } + + /** + * @todo Get browser name only + * @return int browser num + */ + public static function DetectBrowserI() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) + return 0; + + $browser_list = array( + '(Firefox)', + '(Edg|Edge)', + '(Chrome)(?!.*Edge)', + '(Safari)(?!.*Chrome)', + '(OPR|Opera)', + '(Brave)', + '(MSIE|Trident)', + '(Deepnet)', + '(Flock)', + '(Maxthon)', + '(Avant)', + '(AOL)', + '(Vivaldi)', + '(UCBrowser)', + '(YaBrowser)', + '(SamsungBrowser)', + ); + + foreach ($browser_list as $index => $pattern) { + if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) { + return $index + 1; + } + } + + return 0; + } + + /** + * @todo Find browser version + * @return string version + */ + public static function BrowserVersion() { + if (!isset($_SERVER['HTTP_USER_AGENT'])) { + return '0'; + } + + $ua = $_SERVER['HTTP_USER_AGENT']; + $browser = self::DetectBrowser(); + + $version = null; + + switch ($browser) { + case 'Edge': + if (preg_match('/(Edge|Edg)\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[2]; + } + break; + case 'Chrome': + if (preg_match('/Chrome\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; + } + break; + case 'Firefox': + if (preg_match('/Firefox\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; + } + break; + case 'Safari': + if (preg_match('/Version\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; + } + break; + case 'Opera': + if (preg_match('/(OPR|Opera)\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[2]; + } + break; + case 'Internet Explorer': + if (preg_match('/MSIE (\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; + } elseif (preg_match('/rv:(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; // For IE 11 + } + break; + default: + // Generic version detection for other browsers + if (preg_match('/' . preg_quote($browser, '/') . '\/(\d+(\.\d+)*)/', $ua, $matches)) { + $version = $matches[1]; + } + break; + } + + return $version; + } + + /** + * Get searched keywords from referrer URL + * @param string $referer + * @return array|null + */ + public static function GetKeyword($referer = null) { + if ($referer === null) { + $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; + } + + if (empty($referer)) { + return null; + } + + $engines = [ + 'google' => ['q', 'query'], + 'bing' => ['q'], + 'yahoo' => ['p'], + 'yandex' => ['text'], + 'baidu' => ['wd', 'word'], + 'duckduckgo' => ['q'], + 'ask' => ['q'], + 'aol' => ['q'], + 'naver' => ['query'], + 'ecosia' => ['q'], + ]; + + $parsed_url = parse_url($referer); + $host = isset($parsed_url['host']) ? strtolower($parsed_url['host']) : ''; + $query = isset($parsed_url['query']) ? $parsed_url['query'] : ''; + + parse_str($query, $query_params); + + foreach ($engines as $engine => $params) { + if (strpos($host, $engine) !== false) { + foreach ($params as $param) { + if (isset($query_params[$param]) && !empty($query_params[$param])) { + return [ + 'engine' => $engine, + 'keyword' => urldecode($query_params[$param]) + ]; + } + } + } + } + + return null; + } + +// /** +// * @param string $class alternative class +// * return vistor os icon +// * @uses awesome font +// */ +// static public function GetOSIcon($class = '') { +// // get os +// $os_int = self::DetectOSI(); +// +// $win = range(1, 16); +// $linux = range(17, 19); +// $osx = array(20, 21); +// $android = array(25); +// $searchbot = array(27); +// $other = array(0, 22, 23, 24, 26); +// +// switch (true) { +// case in_array($os_int, $win): +// $icon = 'windows'; +// break; +// case in_array($os_int, $linux): +// $icon = 'linux'; +// break; +// case in_array($os_int, $osx): +// $icon = 'apple'; +// break; +// +// case in_array($os_int, $android): +// $icon = 'android'; +// break; +// +// case in_array($os_int, $searchbot): +// $icon = 'google'; +// break; +// default: +// $icon = 'question'; +// break; +// } +// +// $result = ''; +// +// return $result; +// } +// /** +// * @param string $class alternative class +// * return vistor browser icon +// * @uses awesome font +// */ +// static public function GetBrowerIcon($class = '') { +// // get os +// $bowser = self::DetectBrowser(); +// +// +// +// $result = ''; +// return $result; +// } + +} diff --git a/app/Http/Controllers/Api/VisitorController.php b/app/Http/Controllers/Api/VisitorController.php new file mode 100644 index 0000000..1c5b58d --- /dev/null +++ b/app/Http/Controllers/Api/VisitorController.php @@ -0,0 +1,20 @@ +ip())->orderByDesc('id')->first(); + if ($visitor != null){ + $visitor->display = $request->input('display',null); + $visitor->save(); + } + return ['OK'=>true]; + } +} diff --git a/app/Http/Controllers/VisitorController.php b/app/Http/Controllers/VisitorController.php new file mode 100644 index 0000000..3df5f3b --- /dev/null +++ b/app/Http/Controllers/VisitorController.php @@ -0,0 +1,10 @@ +',date("Y-m-d H:i:s" ,time() - (60*60))) + ->where('ip', $request->ip())->first(); + if ($visitor === null) { + $visitor = new Visitor(); + $visitor->ip = $request->ip(); + $visitor->browser = TVisitor::DetectBrowserI(); + $visitor->os = TVisitor::DetectOSI(); + $visitor->version = TVisitor::BrowserVersion(); + $visitor->keywords = TVisitor::GetKeyword(); + $visitor->is_mobile = TVisitor::IsMobile(); + $visitor->save(); + }else{ + $visitor->increment('visit'); + } + return $next($request); + } +} diff --git a/app/Models/Visitor.php b/app/Models/Visitor.php new file mode 100644 index 0000000..adb03f9 --- /dev/null +++ b/app/Models/Visitor.php @@ -0,0 +1,11 @@ + + */ +class VisitorFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + + $displays = ['1920x1080','1366x768','1920x1080','1366x768','1280x1024',null, null]; + $date = $this->faker->dateTimeBetween('-31 days', 'now'); + return [ + // + 'ip' => $this->faker->ipv4(), + 'visit' => rand(1,rand(2,12)), + 'browser' => rand(0,5), + 'os' => rand(0,14), + 'version' => rand(100,132), + 'display' => $displays[count($displays)-1], + 'updated_at' => $date, + 'created_at' => $date, + ]; + } +} diff --git a/database/migrations/2024_07_16_042925_create_visitors_table.php b/database/migrations/2024_07_16_042925_create_visitors_table.php new file mode 100644 index 0000000..f699d38 --- /dev/null +++ b/database/migrations/2024_07_16_042925_create_visitors_table.php @@ -0,0 +1,35 @@ +id(); + $table->ipAddress('ip'); + $table->unsignedInteger('visit')->default(1); + $table->unsignedInteger('browser')->nullable(); + $table->unsignedInteger('os')->nullable(); + $table->string('version')->nullable(); + $table->string('display')->nullable(); + $table->string('keywords')->nullable(); + $table->boolean('is_mobile')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('visitors'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index fe9eb63..a14da41 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -37,6 +37,7 @@ class DatabaseSeeder extends Seeder GfxSeeder::class, AreaSeeder::class, PartSeeder::class, + VisitorSeeder::class ] ); } diff --git a/database/seeders/VisitorSeeder.php b/database/seeders/VisitorSeeder.php new file mode 100644 index 0000000..8c2d995 --- /dev/null +++ b/database/seeders/VisitorSeeder.php @@ -0,0 +1,19 @@ +count(110)->create(); + } +} diff --git a/public/upload/images/index.ParallaxShort.jpg b/public/upload/images/index.ParallaxShort.jpg new file mode 100644 index 0000000..8f60d93 Binary files /dev/null and b/public/upload/images/index.ParallaxShort.jpg differ diff --git a/resources/js/client-custom/windowLoader.js b/resources/js/client-custom/windowLoader.js new file mode 100644 index 0000000..57d3b31 --- /dev/null +++ b/resources/js/client-custom/windowLoader.js @@ -0,0 +1,49 @@ +window.addEventListener('load', function () { + const API_COOKIE_NAME = 'last_api_call'; + const COOKIE_EXPIRY_MINUTES = 59; + + function setCookie(name, value, minutes) { + let expires = ""; + if (minutes) { + let date = new Date(); + date.setTime(date.getTime() + (minutes * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + (value || "") + expires + "; path=/"; + } + + function getCookie(name) { + let nameEQ = name + "="; + let ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; + } + + function canSendData() { + let lastCall = getCookie(API_COOKIE_NAME); + if (!lastCall) return true; + + let lastCallTime = new Date(parseInt(lastCall)); + let currentTime = new Date(); + let timeDiff = (currentTime - lastCallTime) / (1000 * 60); // difference in minutes + + return timeDiff >= COOKIE_EXPIRY_MINUTES; + } + + if (canSendData()) { + axios.post(document.querySelector('#api-display-url').value, { + display: window.screen.availWidth + 'x' + window.screen.availHeight, + }).then(function (response) { + // If the API call is successful, set the cookie + setCookie(API_COOKIE_NAME, new Date().getTime(), COOKIE_EXPIRY_MINUTES); + }).catch(function (error) { + console.error('Error sending data:', error); + }); + } else { + console.log('Data was sent recently. Skipping this time.'); + } +}); diff --git a/resources/js/client.js b/resources/js/client.js index b719cc7..b49c101 100644 --- a/resources/js/client.js +++ b/resources/js/client.js @@ -1,9 +1,16 @@ // PLEASE DO NOT EDIT THIS FILE, -// IF YOU WANT ADD ANY CODE CREATE NEW JS INTO client-custom +// IF YOU WANT ADD ANY CODE CREATE NEW JS INTO client-custom + import axios from 'axios'; + window.axios = axios; + + window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; import "./client-custom/assetsNode.js"; import "./client-custom/confirm.js"; +import "./client-custom/windowLoader.js"; import "../views/segments/preloader/PreloaderCircle/PreloaderCircle.js"; import "../views/segments/top/TopSimple/TopSimple.js"; import "../views/segments/slider/SliderSimple/SliderSimple.js"; import "../views/segments/posts/PostsIconSimple/PostsIconSimple.js"; import "../views/segments/index/GradientTextLink/GradientTextLink.js"; +import "../views/segments/category/FavProductWithMeta/FavProductWithMeta.js"; +import "../views/segments/parallax/ParallaxShort/ParallaxShort.js"; diff --git a/resources/sass/client.scss b/resources/sass/client.scss index d406d05..079ceed 100644 --- a/resources/sass/client.scss +++ b/resources/sass/client.scss @@ -1,19 +1,19 @@ // PLEASE DO NOT EDIT THIS FILE, // IF YOU WANT ADD ANY CODE CREATE NEW SCSS INTO client-custom $xshop-background:#ffffff; -$xshop-primary:#6e0000; +$xshop-primary:#009dff; $xshop-diff:#ffffff; -$xshop-secondary:#ff0000; +$xshop-secondary:#0008ff; $xshop-text:#111111; -$xshop-border-radius:6px; +$xshop-border-radius:0px; $xshop-shadow:2px 2px 4px #777777; :root{ --xshop-background:#ffffff; ---xshop-primary:#6e0000; +--xshop-primary:#009dff; --xshop-diff:#ffffff; ---xshop-secondary:#ff0000; +--xshop-secondary:#0008ff; --xshop-text:#111111; ---xshop-border-radius:6px; +--xshop-border-radius:0px; --xshop-shadow:2px 2px 4px #777777; } @@ -25,3 +25,5 @@ $xshop-shadow:2px 2px 4px #777777; @import "../views/segments/slider/SliderSimple/SliderSimple"; @import "../views/segments/posts/PostsIconSimple/PostsIconSimple"; @import "../views/segments/index/GradientTextLink/GradientTextLink"; +@import "../views/segments/category/FavProductWithMeta/FavProductWithMeta"; +@import "../views/segments/parallax/ParallaxShort/ParallaxShort"; diff --git a/resources/views/website/inc/website-foot.blade.php b/resources/views/website/inc/website-foot.blade.php index e0d118a..bd4a77d 100644 --- a/resources/views/website/inc/website-foot.blade.php +++ b/resources/views/website/inc/website-foot.blade.php @@ -1,3 +1,4 @@ @yield('custom-foot') + diff --git a/routes/api.php b/routes/api.php index d7b2672..cdaa0f3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -37,4 +37,5 @@ Route::prefix('v1')->name('v1.')->group( Route::get('state/{state}', [\App\Http\Controllers\Api\StateController::class,'show'])->name('state.show'); Route::get('category/props/{category}', [\App\Http\Controllers\Api\CategoryController::class,'props'])->name('category.prop'); Route::post('morph/search', [\App\Http\Controllers\Api\MorphController::class,'search'])->name('morph.search'); + Route::post('visitor/display', [\App\Http\Controllers\Api\VisitorController::class,'display'])->name('visitor.display'); }); diff --git a/routes/web.php b/routes/web.php index ec12df7..1f30153 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,7 +5,7 @@ use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); -})->name('welcome'); +})->name('welcome')->middleware(\App\Http\Middleware\VisitorDetector::class); Auth::routes(['register' => false]); @@ -346,3 +346,5 @@ Route::get('test',function (){ }); + +