Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -35,10 +35,7 @@ abstract class AbstractIdShardableSitemapBuilder extends AbstractSitemapBuilder
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->newest(...array_map(
|
||||
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
||||
$this->items(),
|
||||
));
|
||||
return $this->dateTime((clone $this->query())->max($this->lastModifiedColumn()));
|
||||
}
|
||||
|
||||
public function totalItems(): int
|
||||
@@ -69,10 +66,16 @@ abstract class AbstractIdShardableSitemapBuilder extends AbstractSitemapBuilder
|
||||
|
||||
public function lastModifiedForShard(int $shard): ?DateTimeInterface
|
||||
{
|
||||
return $this->newest(...array_map(
|
||||
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
||||
$this->itemsForShard($shard),
|
||||
));
|
||||
$window = $this->shardWindow($shard);
|
||||
|
||||
if ($window === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->dateTime(
|
||||
$this->applyShardWindow($window['from'], $window['to'])
|
||||
->max($this->lastModifiedColumn()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,4 +135,9 @@ abstract class AbstractIdShardableSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
return $this->idColumn();
|
||||
}
|
||||
|
||||
protected function lastModifiedColumn(): string
|
||||
{
|
||||
return 'updated_at';
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
final class PublishedSitemapResolver
|
||||
{
|
||||
public function __construct(private readonly SitemapReleaseManager $releases)
|
||||
@@ -23,9 +25,23 @@ final class PublishedSitemapResolver
|
||||
*/
|
||||
public function resolveNamed(string $requestedName): ?array
|
||||
{
|
||||
$manifest = $this->releases->activeManifest();
|
||||
$releaseId = Cache::remember(
|
||||
'sitemaps:active-release-id',
|
||||
60,
|
||||
fn (): ?string => $this->releases->activeReleaseId(),
|
||||
);
|
||||
|
||||
if ($manifest === null) {
|
||||
if (! is_string($releaseId) || $releaseId === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$manifest = Cache::remember(
|
||||
'sitemaps:manifest:' . $releaseId,
|
||||
3600,
|
||||
fn (): ?array => $this->releases->readManifest($releaseId),
|
||||
);
|
||||
|
||||
if (! is_array($manifest)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -36,13 +52,27 @@ final class PublishedSitemapResolver
|
||||
|
||||
private function resolveDocumentName(string $documentName): ?array
|
||||
{
|
||||
$releaseId = $this->releases->activeReleaseId();
|
||||
$releaseId = Cache::remember(
|
||||
'sitemaps:active-release-id',
|
||||
60,
|
||||
fn (): ?string => $this->releases->activeReleaseId(),
|
||||
);
|
||||
|
||||
if ($releaseId === null) {
|
||||
if (! is_string($releaseId) || $releaseId === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = $this->releases->getDocument($releaseId, $documentName);
|
||||
$ttl = max((int) config('sitemaps.cache_ttl_seconds', 900), 3600);
|
||||
$cacheKey = 'sitemaps:doc:' . $releaseId . ':' . $documentName;
|
||||
|
||||
$content = Cache::get($cacheKey);
|
||||
|
||||
if (! is_string($content) || $content === '') {
|
||||
$content = $this->releases->getDocument($releaseId, $documentName);
|
||||
if (is_string($content) && $content !== '') {
|
||||
Cache::put($cacheKey, $content, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
return is_string($content) && $content !== ''
|
||||
? ['content' => $content, 'release_id' => $releaseId, 'document_name' => $documentName]
|
||||
|
||||
@@ -13,6 +13,7 @@ final class SitemapPublishService
|
||||
private readonly SitemapReleaseCleanupService $cleanup,
|
||||
private readonly SitemapReleaseManager $releases,
|
||||
private readonly SitemapReleaseValidator $validator,
|
||||
private readonly SitemapStaticPublisher $staticPublisher,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -59,7 +60,12 @@ final class SitemapPublishService
|
||||
$this->releases->activate($releaseId);
|
||||
$deleted = $this->cleanup->cleanup();
|
||||
|
||||
return $manifest + ['cleanup_deleted' => $deleted];
|
||||
$staticResult = [];
|
||||
if ($this->staticPublisher->enabled()) {
|
||||
$staticResult = $this->staticPublisher->publish($releaseId);
|
||||
}
|
||||
|
||||
return $manifest + ['cleanup_deleted' => $deleted, 'static_published' => $staticResult];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -70,6 +71,8 @@ final class SitemapReleaseManager
|
||||
];
|
||||
|
||||
$this->atomicJsonWrite($this->activePointerPath(), $payload);
|
||||
|
||||
Cache::forget('sitemaps:active-release-id');
|
||||
}
|
||||
|
||||
public function activeReleaseId(): ?string
|
||||
|
||||
63
app/Services/Sitemaps/SitemapStaticPublisher.php
Normal file
63
app/Services/Sitemaps/SitemapStaticPublisher.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Writes every document from a published release to the public disk so nginx
|
||||
* can serve sitemap.xml and sitemaps/{name}.xml as plain static files,
|
||||
* bypassing PHP entirely on subsequent requests.
|
||||
*/
|
||||
final class SitemapStaticPublisher
|
||||
{
|
||||
public function __construct(private readonly SitemapReleaseManager $releases)
|
||||
{
|
||||
}
|
||||
|
||||
public function enabled(): bool
|
||||
{
|
||||
return (bool) config('sitemaps.static_publish.enabled', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all documents from the given release to the public disk.
|
||||
*
|
||||
* @return array{written: int, skipped: int}
|
||||
*/
|
||||
public function publish(string $releaseId): array
|
||||
{
|
||||
$manifest = $this->releases->readManifest($releaseId);
|
||||
|
||||
if ($manifest === null) {
|
||||
return ['written' => 0, 'skipped' => 0];
|
||||
}
|
||||
|
||||
$disk = Storage::disk($this->publicDisk());
|
||||
$documents = (array) ($manifest['documents'] ?? []);
|
||||
|
||||
$written = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($documents as $documentName => $relativePath) {
|
||||
$content = $this->releases->getDocument($releaseId, (string) $documentName);
|
||||
|
||||
if (! is_string($content) || $content === '') {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$disk->put((string) $relativePath, $content);
|
||||
$written++;
|
||||
}
|
||||
|
||||
return ['written' => $written, 'skipped' => $skipped];
|
||||
}
|
||||
|
||||
private function publicDisk(): string
|
||||
{
|
||||
return (string) config('sitemaps.static_publish.disk', 'sitemaps_public');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user