diff --git a/.gitignore b/.gitignore index be86bc1e4..2547844b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /node_modules /public/hot /public/storage +/public/robots.txt +/public/sitemap.xml /storage/*.key /vendor .DS_Store diff --git a/app/Console/Commands/GenerateRobots.php b/app/Console/Commands/GenerateRobots.php new file mode 100644 index 000000000..04afc2ad4 --- /dev/null +++ b/app/Console/Commands/GenerateRobots.php @@ -0,0 +1,32 @@ +render()); + + return 0; + } +} diff --git a/app/Console/Commands/GenerateSitemap.php b/app/Console/Commands/GenerateSitemap.php new file mode 100644 index 000000000..abe5d18ef --- /dev/null +++ b/app/Console/Commands/GenerateSitemap.php @@ -0,0 +1,60 @@ + ['en' => 'en', 'asl' => 'asl', 'fr' => 'fr', 'lsq' => 'lsq']]; + // once deployed to the server, files have the same modified date, use README as a default last modified date + $lastmod = ['default' => Carbon::createFromTimestamp(filemtime('./README.md'))->toISOString()]; + foreach (Route::getRoutes()->get('GET') as $route) { + if ($route->named(config('seo.sitemap.patterns'))) { + $routeURI = $route->uri(); + [$locale, $url] = explode('/', $routeURI, 2); + if (array_key_exists($url, $routes)) { + $routes[$url][$locale] = $routeURI; + } else { + $routes[$url] = [$locale => $routeURI]; + if ($route->named(config('seo.sitemap.pages'))) { + $routeName = explode('.', $route->getName()); + /* + * TODO: come up with better query for the page, as slug may not follow the pattern in route name + * or changes to the slug would break this logic: https://github.com/accessibility-exchange/platform/issues/1973 + */ + $page = Page::firstWhere('slug->en', '=', end($routeName)); + $lastmod[$url] = $page?->updated_at?->toISOString(); + } + } + } + } + file_put_contents('./public/sitemap.xml', view('sitemap', ['routes' => $routes, 'lastmod' => $lastmod])->render()); + + return 0; + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 078eaad00..907716d51 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -39,6 +39,16 @@ protected function schedule(Schedule $schedule) ->daily() // Run daily at midnight ->timezone('America/Los_Angeles') // Run as PST timezone ->onOneServer(); // run only on a single server at once + + $schedule->command('seo:generate-sitemap') // generate sitemap + ->daily() // Run daily at midnight + ->environments(['staging', 'dev', 'production']) // only run for APP_ENV tagged staging, dev, local, or production + ->timezone('America/Los_Angeles'); // Run as PST timezone + + $schedule->command('seo:generate-robots') // generate robots + ->daily() // Run daily at midnight + ->environments(['staging', 'dev', 'production']) // only run for APP_ENV tagged staging, dev, local, or production + ->timezone('America/Los_Angeles'); // Run as PST timezone } /** diff --git a/config/seo.php b/config/seo.php new file mode 100644 index 000000000..b0e277844 --- /dev/null +++ b/config/seo.php @@ -0,0 +1,21 @@ + [ + 'patterns' => '*.about.*', + 'pages' => [ + '*.about.terms-of-service', + '*.about.privacy-policy', + ], + ], +]; diff --git a/resources/views/robots.blade.php b/resources/views/robots.blade.php new file mode 100644 index 000000000..78c848c7e --- /dev/null +++ b/resources/views/robots.blade.php @@ -0,0 +1,10 @@ + +Sitemap: {{ env('APP_URL') }}/sitemap.xml + +User-Agent: * +Disallow: admin/ +Disallow: api/ +Disallow: status/ + +User-Agent: GPTBot +Disallow: / diff --git a/resources/views/sitemap.blade.php b/resources/views/sitemap.blade.php new file mode 100644 index 000000000..71f63b5c6 --- /dev/null +++ b/resources/views/sitemap.blade.php @@ -0,0 +1,20 @@ +' . "\n" ?> + + @foreach ($routes as $baseUrl => $localizedUrls) + + @if ($baseUrl !== 'about/page/{page}') + @foreach ($localizedUrls as $locale => $localizedUrl) + @if ($locale === 'en') + {{ env('APP_URL') . '/' . $localizedUrl }} + {{ $lastmod[$baseUrl] ?? $lastmod['default'] }} + @endif + + @endforeach + @endif + + @endforeach + diff --git a/routes/defined-terms.php b/routes/defined-terms.php index 80443c89b..bfdfb7384 100644 --- a/routes/defined-terms.php +++ b/routes/defined-terms.php @@ -4,4 +4,4 @@ use Illuminate\Support\Facades\Route; Route::multilingual('/about/glossary', [DefinedTermController::class, 'index']) - ->name('defined-terms.index'); + ->name('about.defined-terms.index'); diff --git a/tests/Feature/DefinedTermTest.php b/tests/Feature/DefinedTermTest.php index 86bcec87f..289b476b5 100644 --- a/tests/Feature/DefinedTermTest.php +++ b/tests/Feature/DefinedTermTest.php @@ -7,6 +7,6 @@ $user = User::factory()->create(); $term = DefinedTerm::factory()->create(); - $response = $this->actingAs($user)->get(localized_route('defined-terms.index')); + $response = $this->actingAs($user)->get(localized_route('about.defined-terms.index')); $response->assertSee($term->term); });