Files
SkinbaseNova/app/Services/Sitemaps/SitemapReleaseManager.php

186 lines
5.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Sitemaps;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
final class SitemapReleaseManager
{
public function generateReleaseId(): string
{
return now()->utc()->format('YmdHis') . '-' . Str::lower((string) Str::ulid());
}
public function releaseExists(string $releaseId): bool
{
return Storage::disk($this->disk())->exists($this->manifestPath($releaseId));
}
public function putDocument(string $releaseId, string $documentName, string $content): void
{
Storage::disk($this->disk())->put($this->releaseDocumentPath($releaseId, $documentName), $content);
}
public function getDocument(string $releaseId, string $documentName): ?string
{
$disk = Storage::disk($this->disk());
$path = $this->releaseDocumentPath($releaseId, $documentName);
if (! $disk->exists($path)) {
return null;
}
$content = $disk->get($path);
return is_string($content) && $content !== '' ? $content : null;
}
public function writeManifest(string $releaseId, array $manifest): void
{
Storage::disk($this->disk())->put($this->manifestPath($releaseId), json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
public function readManifest(string $releaseId): ?array
{
$disk = Storage::disk($this->disk());
$path = $this->manifestPath($releaseId);
if (! $disk->exists($path)) {
return null;
}
$decoded = json_decode((string) $disk->get($path), true);
return is_array($decoded) ? $decoded : null;
}
public function writeBuildReport(string $releaseId, array $report): void
{
Storage::disk($this->disk())->put($this->buildReportPath($releaseId), json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
public function activate(string $releaseId): void
{
$payload = [
'release_id' => $releaseId,
'activated_at' => now()->toAtomString(),
];
$this->atomicJsonWrite($this->activePointerPath(), $payload);
}
public function activeReleaseId(): ?string
{
$disk = Storage::disk($this->disk());
$path = $this->activePointerPath();
if (! $disk->exists($path)) {
return null;
}
$decoded = json_decode((string) $disk->get($path), true);
return is_array($decoded) && is_string($decoded['release_id'] ?? null)
? $decoded['release_id']
: null;
}
public function activeManifest(): ?array
{
$releaseId = $this->activeReleaseId();
return $releaseId !== null ? $this->readManifest($releaseId) : null;
}
/**
* @return list<array<string, mixed>>
*/
public function listReleases(): array
{
$releases = [];
foreach (Storage::disk($this->disk())->allDirectories($this->releasesRootPath()) as $directory) {
$releaseId = basename($directory);
$manifest = $this->readManifest($releaseId);
if ($manifest !== null) {
$releases[] = $manifest;
}
}
usort($releases, static fn (array $left, array $right): int => strcmp((string) ($right['release_id'] ?? ''), (string) ($left['release_id'] ?? '')));
return $releases;
}
public function deleteRelease(string $releaseId): void
{
Storage::disk($this->disk())->deleteDirectory($this->releaseRootPath($releaseId));
}
public function documentRelativePath(string $documentName): string
{
return $documentName === SitemapCacheService::INDEX_DOCUMENT
? 'sitemap.xml'
: 'sitemaps/' . $documentName . '.xml';
}
public function releaseDocumentPath(string $releaseId, string $documentName): string
{
return $this->releaseRootPath($releaseId) . '/' . $this->documentRelativePath($documentName);
}
public function manifestPath(string $releaseId): string
{
return $this->releaseRootPath($releaseId) . '/manifest.json';
}
public function buildReportPath(string $releaseId): string
{
return $this->releaseRootPath($releaseId) . '/build-report.json';
}
private function releaseRootPath(string $releaseId): string
{
return $this->releasesRootPath() . '/' . $releaseId;
}
private function releasesRootPath(): string
{
return trim((string) config('sitemaps.releases.path', 'sitemaps'), '/') . '/releases';
}
private function activePointerPath(): string
{
return trim((string) config('sitemaps.releases.path', 'sitemaps'), '/') . '/active.json';
}
private function disk(): string
{
return (string) config('sitemaps.releases.disk', 'local');
}
private function atomicJsonWrite(string $relativePath, array $payload): void
{
$disk = Storage::disk($this->disk());
$json = json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
try {
$absolutePath = $disk->path($relativePath);
$directory = dirname($absolutePath);
if (! is_dir($directory)) {
mkdir($directory, 0755, true);
}
$temporaryPath = $absolutePath . '.tmp';
file_put_contents($temporaryPath, $json, LOCK_EX);
rename($temporaryPath, $absolutePath);
} catch (\Throwable) {
$disk->put($relativePath, $json);
}
}
}