press-releases:check-index-status fragt täglich je Portal ein Kontingent veröffentlichter Meldungen über die URL Inspection API ab, nach Alter gestaffelt, nie geprüfte zuerst. Jede Antwort wird als Momentaufnahme in press_release_index_checks gespeichert; der reduzierte Befund (IndexState) trennt „noch nicht gecrawlt" von „gecrawlt, nicht indexiert". Client authentifiziert sich als Service-Konto über ein selbst signiertes JWT — kein Google-SDK. Admin ▸ Reports ▸ Indexierung zeigt die Verteilung je Portal getrennt nach Direkteinreichung und Syndikation (Status-Log source=api; Altbestand ohne Log zählt als direkt). Das Kundenpanel heißt jetzt „Auslieferung", zeigt Sitemap-Datum und die Abrufe durch Such- und KI-Dienste mit dem Satz „Ein Abruf ist kein Zitat"; der Google-Index-Block hängt an GSC_CUSTOMER_VISIBLE (false). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
62 lines
2.3 KiB
PHP
62 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Enums;
|
|
|
|
/**
|
|
* Befund einer URL-Inspektion, auf das reduziert, was in jeder Anzeige
|
|
* unterschieden werden muss (Anhang 17.09.2026, 1c.4): „noch nicht
|
|
* gecrawlt" und „gecrawlt, aber nicht indexiert" sind verschiedene Befunde.
|
|
*/
|
|
enum IndexState: string
|
|
{
|
|
case Indexed = 'indexed';
|
|
case CrawledNotIndexed = 'crawled_not_indexed';
|
|
case NotCrawled = 'not_crawled';
|
|
case Excluded = 'excluded';
|
|
case Unknown = 'unknown';
|
|
|
|
/**
|
|
* @param array<string, mixed> $indexStatus `inspectionResult.indexStatusResult` der URL Inspection API
|
|
*/
|
|
public static function fromIndexStatus(array $indexStatus): self
|
|
{
|
|
$verdict = strtoupper((string) ($indexStatus['verdict'] ?? ''));
|
|
$coverage = mb_strtolower((string) ($indexStatus['coverageState'] ?? ''));
|
|
$indexing = strtoupper((string) ($indexStatus['indexingState'] ?? ''));
|
|
$robots = strtoupper((string) ($indexStatus['robotsTxtState'] ?? ''));
|
|
$fetch = strtoupper((string) ($indexStatus['pageFetchState'] ?? ''));
|
|
$crawled = ! empty($indexStatus['lastCrawlTime']);
|
|
|
|
if ($verdict === 'PASS' || (str_contains($coverage, 'indexed') && ! str_contains($coverage, 'not indexed'))) {
|
|
return self::Indexed;
|
|
}
|
|
|
|
if ($robots === 'DISALLOWED'
|
|
|| in_array($indexing, ['BLOCKED_BY_META_TAG', 'BLOCKED_BY_HTTP_HEADER', 'BLOCKED_BY_ROBOTS_TXT'], true)
|
|
|| ($fetch !== '' && $fetch !== 'SUCCESSFUL' && $crawled)
|
|
|| str_contains($coverage, 'duplicate')
|
|
|| str_contains($coverage, 'canonical')
|
|
|| str_contains($coverage, 'redirect')
|
|
|| str_contains($coverage, 'noindex')
|
|
|| str_contains($coverage, 'blocked')) {
|
|
return self::Excluded;
|
|
}
|
|
|
|
if (! $crawled) {
|
|
return $verdict === '' && $coverage === '' ? self::Unknown : self::NotCrawled;
|
|
}
|
|
|
|
return self::CrawledNotIndexed;
|
|
}
|
|
|
|
public function label(): string
|
|
{
|
|
return match ($this) {
|
|
self::Indexed => 'indexiert',
|
|
self::CrawledNotIndexed => 'gecrawlt, nicht indexiert',
|
|
self::NotCrawled => 'noch nicht gecrawlt',
|
|
self::Excluded => 'ausgeschlossen',
|
|
self::Unknown => 'unbekannt',
|
|
};
|
|
}
|
|
}
|