diff --git a/.gitignore b/.gitignore index 3847c0d..8a5c823 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ yarn-error.log /.nova /.vscode /.zed + +# Sitemap files (auto-generated) +/public/sitemap.xml +/public/sitemap_*.xml diff --git a/app/Console/Commands/GenerateMemesSitemap.php b/app/Console/Commands/GenerateMemesSitemap.php new file mode 100644 index 0000000..e0dc54c --- /dev/null +++ b/app/Console/Commands/GenerateMemesSitemap.php @@ -0,0 +1,63 @@ +info('Starting memes sitemap generation...'); + + $sitemap = Sitemap::create(); + + // Get the base URL from config + $baseUrl = config('app.url'); + + // Get all enabled memes + $memes = $this->memeMediaService->getAllEnabledMemes(); + + $this->info("Found {$memes->count()} enabled memes"); + + // Add a progress bar for large datasets + $progressBar = $this->output->createProgressBar($memes->count()); + $progressBar->start(); + + // Add each meme to the sitemap + foreach ($memes as $meme) { + $url = Url::create($baseUrl . '/meme/' . $meme->slug) + ->setPriority(0.8) + ->setChangeFrequency(Url::CHANGE_FREQUENCY_MONTHLY) + ->setLastModificationDate($meme->updated_at); + + $sitemap->add($url); + $progressBar->advance(); + } + + $progressBar->finish(); + $this->newLine(); + + // Save the sitemap + $sitemapPath = public_path('sitemap_memes.xml'); + $sitemap->writeToFile($sitemapPath); + + $this->info("Memes sitemap generated successfully!"); + $this->info("Sitemap saved to: {$sitemapPath}"); + $this->info("Total URLs: {$memes->count()}"); + + return Command::SUCCESS; + } +} diff --git a/app/Console/Commands/GenerateSitemap.php b/app/Console/Commands/GenerateSitemap.php new file mode 100644 index 0000000..30a89df --- /dev/null +++ b/app/Console/Commands/GenerateSitemap.php @@ -0,0 +1,70 @@ +info('Starting main sitemap generation...'); + + $sitemapIndex = SitemapIndex::create(); + + // Get the base URL from config + $baseUrl = config('app.url'); + + // List of sub-sitemaps to include in the main sitemap + $subSitemaps = [ + [ + 'url' => $baseUrl . '/sitemap_static.xml', + 'lastModified' => $this->getSitemapLastModified('sitemap_static.xml'), + ], + [ + 'url' => $baseUrl . '/sitemap_memes.xml', + 'lastModified' => $this->getSitemapLastModified('sitemap_memes.xml'), + ], + // Future sitemaps can be added here: + // [ + // 'url' => $baseUrl . '/sitemap_pages.xml', + // 'lastModified' => now(), + // ], + ]; + + // Add each sub-sitemap to the index + foreach ($subSitemaps as $sitemap) { + $sitemapIndex->add($sitemap['url'], $sitemap['lastModified']); + $this->info("Added sub-sitemap: {$sitemap['url']}"); + } + + // Save the main sitemap index + $sitemapPath = public_path('sitemap.xml'); + $sitemapIndex->writeToFile($sitemapPath); + + $this->info("Main sitemap index generated successfully!"); + $this->info("Sitemap saved to: {$sitemapPath}"); + $this->info("Total sub-sitemaps: " . count($subSitemaps)); + + return Command::SUCCESS; + } + + /** + * Get the last modification date of a sitemap file + */ + private function getSitemapLastModified(string $filename): \DateTime + { + $filepath = public_path($filename); + + if (file_exists($filepath)) { + return new \DateTime('@' . filemtime($filepath)); + } + + // If file doesn't exist, return current time + return new \DateTime(); + } +} diff --git a/app/Console/Commands/GenerateStaticSitemap.php b/app/Console/Commands/GenerateStaticSitemap.php new file mode 100644 index 0000000..bf5d03c --- /dev/null +++ b/app/Console/Commands/GenerateStaticSitemap.php @@ -0,0 +1,79 @@ +info('Starting static sitemap generation...'); + + $sitemap = Sitemap::create(); + + // Get the base URL from config + $baseUrl = config('app.url'); + + // Add static pages + $staticPages = [ + [ + 'url' => $baseUrl, + 'priority' => 1.0, + 'changeFrequency' => Url::CHANGE_FREQUENCY_WEEKLY, + 'lastModificationDate' => now(), + ], + [ + 'url' => $baseUrl . '/meme-library', + 'priority' => 0.9, + 'changeFrequency' => Url::CHANGE_FREQUENCY_DAILY, + 'lastModificationDate' => now(), + ], + [ + 'url' => $baseUrl . '/privacy', + 'priority' => 0.3, + 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY, + 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated + ], + [ + 'url' => $baseUrl . '/terms', + 'priority' => 0.3, + 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY, + 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated + ], + [ + 'url' => $baseUrl . '/dmca-copyright', + 'priority' => 0.2, + 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY, + 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated + ], + ]; + + // Add each static page to the sitemap + foreach ($staticPages as $page) { + $url = Url::create($page['url']) + ->setPriority($page['priority']) + ->setChangeFrequency($page['changeFrequency']) + ->setLastModificationDate($page['lastModificationDate']); + + $sitemap->add($url); + + $this->info("Added: {$page['url']}"); + } + + // Save the sitemap + $sitemapPath = public_path('sitemap_static.xml'); + $sitemap->writeToFile($sitemapPath); + + $this->info("Static sitemap generated successfully!"); + $this->info("Sitemap saved to: {$sitemapPath}"); + $this->info("Total URLs: " . count($staticPages)); + + return Command::SUCCESS; + } +} diff --git a/app/Services/MemeMediaService.php b/app/Services/MemeMediaService.php index ba3b0bf..cc9fb09 100644 --- a/app/Services/MemeMediaService.php +++ b/app/Services/MemeMediaService.php @@ -150,6 +150,16 @@ private function buildSearchQuery(?string $search = null): Builder return $query; } + /** + * Get all enabled memes for sitemap generation + */ + public function getAllEnabledMemes(): Collection + { + return MemeMedia::where('is_enabled', true) + ->orderBy('updated_at', 'desc') + ->get(); + } + /** * Get base query for enabled memes */ diff --git a/composer.json b/composer.json index f8ec87b..5674c40 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "pbmedia/laravel-ffmpeg": "^8.7", "pgvector/pgvector": "^0.2.2", "spatie/laravel-responsecache": "^7.7", + "spatie/laravel-sitemap": "^7.3", "spatie/laravel-tags": "^4.10", "symfony/dom-crawler": "^7.3", "tightenco/ziggy": "^2.4", diff --git a/composer.lock b/composer.lock index c942779..ba2ec69 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "90bdf51ac5b2a4e868e9bb012981fc26", + "content-hash": "79a02fbcd504e47b61331dad19bd62a0", "packages": [ { "name": "artesaos/seotools", @@ -4327,6 +4327,60 @@ }, "time": "2025-03-30T21:06:30+00:00" }, + { + "name": "nicmart/tree", + "version": "0.9.0", + "source": { + "type": "git", + "url": "https://github.com/nicmart/Tree.git", + "reference": "f5e17bf18d78cfb0666ebb9f956c3acd8d14229d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nicmart/Tree/zipball/f5e17bf18d78cfb0666ebb9f956c3acd8d14229d", + "reference": "f5e17bf18d78cfb0666ebb9f956c3acd8d14229d", + "shasum": "" + }, + "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.44.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.28.1", + "fakerphp/faker": "^1.24.1", + "infection/infection": "~0.26.19", + "phpunit/phpunit": "^9.6.19", + "psalm/plugin-phpunit": "~0.19.0", + "vimeo/psalm": "^5.26.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Tree\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolò Martini", + "email": "nicmartnic@gmail.com" + }, + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "A basic but flexible php tree data structure and a fluent tree builder implementation.", + "support": { + "issues": "https://github.com/nicmart/Tree/issues", + "source": "https://github.com/nicmart/Tree/tree/0.9.0" + }, + "time": "2024-11-22T15:36:01+00:00" + }, { "name": "nikic/php-parser", "version": "v5.4.0", @@ -5857,6 +5911,142 @@ ], "time": "2024-04-27T21:32:50+00:00" }, + { + "name": "spatie/browsershot", + "version": "5.0.10", + "source": { + "type": "git", + "url": "https://github.com/spatie/browsershot.git", + "reference": "9e5ae15487b3cdc3eb03318c1c8ac38971f60e58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/browsershot/zipball/9e5ae15487b3cdc3eb03318c1c8ac38971f60e58", + "reference": "9e5ae15487b3cdc3eb03318c1c8ac38971f60e58", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-json": "*", + "php": "^8.2", + "spatie/temporary-directory": "^2.0", + "symfony/process": "^6.0|^7.0" + }, + "require-dev": { + "pestphp/pest": "^3.0", + "spatie/image": "^3.6", + "spatie/pdf-to-text": "^1.52", + "spatie/phpunit-snapshot-assertions": "^4.2.3|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Browsershot\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://github.com/freekmurze", + "role": "Developer" + } + ], + "description": "Convert a webpage to an image or pdf using headless Chrome", + "homepage": "https://github.com/spatie/browsershot", + "keywords": [ + "chrome", + "convert", + "headless", + "image", + "pdf", + "puppeteer", + "screenshot", + "webpage" + ], + "support": { + "source": "https://github.com/spatie/browsershot/tree/5.0.10" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-05-15T07:10:57+00:00" + }, + { + "name": "spatie/crawler", + "version": "8.4.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/crawler.git", + "reference": "4f4c3ead439e7e57085c0b802bc4e5b44fb7d751" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/crawler/zipball/4f4c3ead439e7e57085c0b802bc4e5b44fb7d751", + "reference": "4f4c3ead439e7e57085c0b802bc4e5b44fb7d751", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.3", + "guzzlehttp/psr7": "^2.0", + "illuminate/collections": "^10.0|^11.0|^12.0", + "nicmart/tree": "^0.9", + "php": "^8.2", + "spatie/browsershot": "^5.0.5", + "spatie/robots-txt": "^2.0", + "symfony/dom-crawler": "^6.0|^7.0" + }, + "require-dev": { + "pestphp/pest": "^2.0|^3.0", + "spatie/ray": "^1.37" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Crawler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be" + } + ], + "description": "Crawl all internal links found on a website", + "homepage": "https://github.com/spatie/crawler", + "keywords": [ + "crawler", + "link", + "spatie", + "website" + ], + "support": { + "issues": "https://github.com/spatie/crawler/issues", + "source": "https://github.com/spatie/crawler/tree/8.4.3" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-05-20T09:00:51+00:00" + }, { "name": "spatie/eloquent-sortable", "version": "4.5.0", @@ -6075,6 +6265,79 @@ ], "time": "2025-05-20T08:39:19+00:00" }, + { + "name": "spatie/laravel-sitemap", + "version": "7.3.6", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-sitemap.git", + "reference": "506b2acdd350c7ff868a7711b4f30e486b20e9b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-sitemap/zipball/506b2acdd350c7ff868a7711b4f30e486b20e9b0", + "reference": "506b2acdd350c7ff868a7711b4f30e486b20e9b0", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.8", + "illuminate/support": "^11.0|^12.0", + "nesbot/carbon": "^2.71|^3.0", + "php": "^8.2||^8.3||^8.4", + "spatie/crawler": "^8.0.1", + "spatie/laravel-package-tools": "^1.16.1", + "symfony/dom-crawler": "^6.3.4|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6.6", + "orchestra/testbench": "^9.0|^10.0", + "pestphp/pest": "^3.7.4", + "spatie/pest-plugin-snapshots": "^2.1", + "spatie/phpunit-snapshot-assertions": "^5.1.2", + "spatie/temporary-directory": "^2.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Sitemap\\SitemapServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\Sitemap\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Create and generate sitemaps with ease", + "homepage": "https://github.com/spatie/laravel-sitemap", + "keywords": [ + "laravel-sitemap", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/laravel-sitemap/tree/7.3.6" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2025-04-10T12:13:41+00:00" + }, { "name": "spatie/laravel-tags", "version": "4.10.0", @@ -6228,6 +6491,66 @@ ], "time": "2025-02-20T15:51:22+00:00" }, + { + "name": "spatie/robots-txt", + "version": "2.5.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/robots-txt.git", + "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/robots-txt/zipball/ef85dfaa48372c0a7fdfb144592f95de1a2e9b79", + "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Robots\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Roose", + "email": "brent@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Determine if a page may be crawled from robots.txt and robots meta tags", + "homepage": "https://github.com/spatie/robots-txt", + "keywords": [ + "robots-txt", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/robots-txt/issues", + "source": "https://github.com/spatie/robots-txt/tree/2.5.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-07-01T07:07:44+00:00" + }, { "name": "spatie/temporary-directory", "version": "2.3.0", diff --git a/public/robots.txt b/public/robots.txt index eb05362..902fedf 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,4 @@ User-agent: * Disallow: + +Sitemap: https://memefast.app/sitemap.xml