- feed_fetches: jeder Feed-Abruf mit Art, Schlüssel, Portal, Sprache, erkanntem Reader (config/feeds.php, FeedReaderIdentifier), vom Dienst gemeldeter Abonnentenzahl und gekürztem User-Agent — keine IP. Schreiben per defer() nach der Antwort, auch bei Cache-Treffer. - Aufbewahrung 395 Tage über press-releases:purge-events; FEED_FETCHES_RECORDED_SINCE. - Admin ▸ Reports ▸ Indexierung & Abrufe: Block „Abrufe der RSS-Feeds" (Summen je Feed-Art, je Reader-Dienst, meistabgerufene Feeds; FeedFetchReport, Verdichtung in PHP für MySQL und SQLite). - Panel „Auslieferung": Abrufe je Feed in 30 Tagen; RSS-Seite nennt die Zählung ohne IP. - Tests: FeedFetchLogTest (5), IndexStatusCheckTest erweitert. - dev/2026-09-01: Nachtrag Protokoll Teil 11, Roadmap 3.1; 0.7 gilt auch für Crawler- und Feed-Log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
60 lines
1.7 KiB
PHP
60 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Support\FeedReaderIdentifier;
|
|
use Database\Factories\FeedFetchFactory;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
/**
|
|
* Ein Abruf eines RSS-Feeds (Roadmap 3.1). Append-Log ohne IP; `reader` ist
|
|
* der Schlüssel aus config/feeds.php, `subscribers` die vom Reader-Dienst
|
|
* selbst gemeldete Abonnentenzahl.
|
|
*/
|
|
class FeedFetch extends Model
|
|
{
|
|
/** @use HasFactory<FeedFetchFactory> */
|
|
use HasFactory;
|
|
|
|
public const ?string UPDATED_AT = null;
|
|
|
|
public const FEED_PORTAL = 'portal';
|
|
|
|
public const FEED_CATEGORY = 'category';
|
|
|
|
public const FEED_NEWSROOM = 'newsroom';
|
|
|
|
protected $fillable = [
|
|
'feed',
|
|
'feed_key',
|
|
'portal',
|
|
'language',
|
|
'reader',
|
|
'subscribers',
|
|
'user_agent',
|
|
];
|
|
|
|
/**
|
|
* Schreibt einen Abruf nach der Antwort (defer) — fehlertolerant, weil
|
|
* die Messung den Abruf begleitet und ihn nie scheitern lassen darf.
|
|
*/
|
|
public static function recordDeferred(string $feed, ?string $feedKey, string $portal, string $language, ?string $userAgent): void
|
|
{
|
|
try {
|
|
$userAgent = trim((string) $userAgent);
|
|
|
|
self::query()->create([
|
|
'feed' => $feed,
|
|
'feed_key' => $feedKey,
|
|
'portal' => $portal,
|
|
'language' => $language,
|
|
'reader' => FeedReaderIdentifier::identify($userAgent),
|
|
'subscribers' => FeedReaderIdentifier::subscribers($userAgent),
|
|
'user_agent' => mb_substr($userAgent, 0, 255),
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
report($e);
|
|
}
|
|
}
|
|
}
|