Zum Hauptinhalt springen

Collector API

REST-API fuer die Browser Extension, die Instagram-Profile scraped und Ergebnisse zurueckliefert.

Authentifizierung

Die API nutzt Laravel Sanctum Token-Authentifizierung mit erweitertem Token-Lifecycle (Soft-Revoke, IP-Whitelist, Usage-Tracking).

Token erstellen

Tokens werden ueber die Admin-Seite /admin/api-management oder per Artisan-Command erstellt:

php artisan collector:token "Chrome Extension 1"
# Gibt den Plaintext-Token aus: 1|abc123def456...

Der Token wird im Authorization-Header mitgesendet:

Authorization: Bearer 1|abc123def456...

Middleware-Chain

auth:sanctum → RejectRevokedTokens → throttle:collector → TrackApiTokenUsage
MiddlewarePruefung
auth:sanctumToken existiert und ist nicht abgelaufen
RejectRevokedTokensToken nicht revoziert (revoked_at IS NULL) + IP-Whitelist
throttle:collector600 Requests pro Minute pro Client
TrackApiTokenUsageStuendliche Nutzungs-Aggregation in api_token_usage_logs

Token-Lifecycle

stateDiagram-v2
[*] --> Active: Token erstellt
Active --> Revoked: Admin deaktiviert
Revoked --> Deleted: Admin loescht
Active --> Expired: expires_at erreicht
note right of Active: API-Zugriff erlaubt
note right of Revoked: API-Zugriff blockiert (401)
note right of Expired: API-Zugriff blockiert (Sanctum)

IP-Whitelist

Tokens koennen optional auf bestimmte IPs beschraenkt werden (allowed_ips JSON-Array). Leere/null Whitelist erlaubt alle IPs. Requests von nicht-gelisteten IPs erhalten 403 Forbidden.

Token-Management

Admin-Dashboard unter /admin/api-management:

  • Token erstellen (Name, Abilities, Ablaufdatum, IP-Whitelist)
  • Token deaktivieren (Soft-Revoke mit revoked_at + revoked_by)
  • Token loeschen (nur revozierte, inkl. verwaister CollectorClients)
  • IP-Whitelist bearbeiten
  • Nutzungs-Charts (24h, 7d, 30d) pro Token und gesamt
  • Collector-Client Health-Status

Deaktivierte Clients: Collector-Clients ohne aktive Tokens (alle revoziert) werden automatisch aus der API-Liste, dem Update-Status-Dashboard und dem Health-Check (/up_system) gefiltert. Der withActiveTokens-Scope auf CollectorClient verwendet einen expliziten uuid::text Cast für PostgreSQL-Kompatibilität (SQLite toleriert den Typ-Unterschied zwischen UUID und varchar).

Location: routes/api.php, app/Livewire/Admin/ApiManagement/Index.php, app/Services/ApiTokenService.php

Endpoints

POST /api/collector/jobs/lease

Least den naechsten verfuegbaren Job. Jobs mit hoeherer Prioritaet werden bevorzugt, danach FIFO. Parallele Collectors blockieren sich nicht gegenseitig dank FOR UPDATE SKIP LOCKED (PostgreSQL) — jeder Collector ueberspringt bereits gelockte Rows und bekommt sofort den naechsten verfuegbaren Job. Fallback auf normales FOR UPDATE fuer SQLite (Tests).

Request:

{
"source": "instagram"
}

Response (200 - Job verfuegbar):

{
"job": {
"id": "9c3f4a2b-...",
"source": "instagram",
"status": "leased",
"priority": 30,
"payload": {
"job_type": "watcher_import",
"run_id": 22,
"workspace_id": 1,
"created_by": 1,
"input_url": "https://www.instagram.com/example/",
"dedupe_key": "handle:example"
},
"lease_expires_at": "2026-02-09T14:30:00Z"
}
}

Response (200 - kein Job verfuegbar):

{
"job": null
}

Expired Leases werden automatisch beim naechsten lease-Call recycled: Jobs mit Status leased deren lease_expires_at in der Vergangenheit liegt, werden erneut vergeben.

Location: app/Http/Controllers/Api/CollectorJobController.php (Methode lease)

GET /api/collector/jobs/stats

Queue-Statistiken gruppiert nach Plattform.

Response:

{
"stats": {
"instagram": {
"queued": 142,
"leased": 3,
"total": 145
},
"youtube": {
"queued": 0,
"leased": 0,
"total": 0
}
}
}

Location: app/Http/Controllers/Api/CollectorJobController.php (Methode stats)

POST /api/collector/jobs/{job}/complete

Markiert einen geleasten Job als abgeschlossen und uebergibt das Scrape-Ergebnis.

Request (watcher_import -- oeffentliches Profil):

{
"result": {
"handle": "example",
"canonical_url": "https://www.instagram.com/example/",
"followers_count": 15420,
"title": "Example Creator",
"is_private": false,
"is_verified": true,
"profile_type": "creator",
"following_count": 312,
"post_count": 847,
"thumbnail_url": "https://scontent-xxx.cdninstagram.com/...",
"profile_data": { "is_private": false },
"daily_raw": { "post_count": 847 }
}
}

Required Fields (immer):

FeldTypBeschreibung
handlestringInstagram-Handle (ohne @)
canonical_urlstringKanonische Profil-URL
followers_countintegerFollower-Anzahl
titlestringDisplay-Name
is_privatebooleanPrivates Profil?
is_verifiedbooleanVerifiziertes Profil?
profile_typestringcreator, business, private, personal

Required Fields (nur bei oeffentlichen Profilen, is_private=false):

FeldTypBeschreibung
following_countintegerAnzahl gefolgter Profile
post_countintegerAnzahl der Posts

Private Profile Handling

Private Profile liefern eingeschraenkte Daten:

{
"result": {
"handle": "private_user",
"canonical_url": "https://www.instagram.com/private_user/",
"followers_count": 200,
"title": "Private User",
"is_private": true,
"is_verified": false,
"profile_type": "private",
"following_count": null,
"post_count": null
}
}

Location: app/Services/Collector/InstagramWatcherImportProcessor.php (Methode validateResult)

Payload-Unterschiede: watcher_import vs. daily_scrape

Feldwatcher_importdaily_scrape
job_type"watcher_import""daily_scrape"
run_idImport-Run-ID--
workspace_idZiel-Workspace--
created_byUser-ID--
social_profile_id--Profil-ID
input_urlInstagram-URLInstagram-URL
dedupe_key"handle:xxx"--

Response (200):

{
"job": {
"id": "9c3f4a2b-...",
"status": "completed",
"result": { ... }
}
}

Error Responses:

CodeBeschreibung
409Job nicht im Status leased, Lease abgelaufen, oder anderer Client
422Validierungsfehler im Result-Payload

POST /api/collector/jobs/{job}/fail

Markiert einen geleasten Job als fehlgeschlagen.

Request:

{
"error_code": "scrape_error",
"error_message": "Login required - session expired"
}

Error Codes:

CodeBeschreibungRe-Queue?
scrape_errorAllgemeiner Scrape-FehlerJa (ausser 404)
rate_limitedInstagram Rate LimitNein
not_foundProfil existiert nichtNein

Re-Queue-Logik: Jobs mit scrape_error werden am Ende der Queue erneut eingereiht (Prioritaet 0), ausser die Fehlermeldung enthaelt not found, user not found, http 404 oder status 404 -- diese werden permanent als fehlgeschlagen markiert.

Location: app/Http/Controllers/Api/CollectorJobController.php (Methode fail)

POST /api/collectors/heartbeat

Aktualisiert den last_seen_at-Timestamp des Collector-Clients.

Request: Leerer Body genuegt.

Response (200):

{
"message": "ok"
}

Location: app/Http/Controllers/Api/CollectorClientController.php

Token Health Monitoring (Plan 37)

Per-Token-Monitoring das fehlerhafte Collector-Tokens automatisch erkennt, pausiert und nach Selbstheilung wieder aktiviert.

Funktionsweise

  1. Error-Tracking: Jeder complete()/fail() Call inkrementiert Cache-Counter pro Collector-Client (Success/Fail/Error-Code-Breakdown).
  2. Evaluation: collector:evaluate-token-health (alle 5 Min) berechnet die Fehlerrate im Rolling Window (60 Min, min. 20 Requests).
  3. Auto-Suspension: Bei >40% Fehlerrate wird der Token suspendiert — der Lease-Endpoint gibt HTTP 429 mit Retry-After: 900 zurueck.
  4. Re-Test: 5 Test-Jobs werden an den suspendierten Token gesendet. Bei 80% Erfolg wird der Token automatisch reaktiviert.
  5. Re-Test-Isolation (F3): Re-Test-Jobs zaehlen nur im Retest-Tracker, nicht in der regulaeren Health-Statistik. Dadurch koennen Re-Test-Failures die Fehlerrate nicht weiter verschlechtern.

Schwellwerte

LevelFehlerrateAktion
Healthy<20%Keine
Warning20-40%Admin-Alarm (Toast)
Critical>40%Auto-Suspension + E-Mail-Alarm + Job-Umverteilung

Zusaetzlich: Error-Code-spezifische Schwellwerte (tab_closed >30%, parse_error >10%, profile_not_found >5%, scrape_error >25%).

Anomalie-Erkennung

Z-Score-basiert: Vergleich der aktuellen Fehlerrate mit dem 24h-Durchschnitt. Alarm bei Z-Score >2 (2 Standardabweichungen ueber Mittel).

Fehler-Korrelation

Erkennt ob Fehler collector-spezifisch oder plattformweit sind. Wenn >50% aller aktiven Collectors gleichzeitig >30% Fehlerrate haben → "Plattform-Problem"-Alarm statt individueller Token-Suspension.

Config

// config/collector.php → token_health
'warning_threshold' => 0.20,
'critical_threshold' => 0.40,
'min_sample_size' => 20,
'evaluation_window_minutes' => 60,
'suspension_ttl_hours' => 24,
'retest_job_count' => 5,
'retest_success_threshold' => 0.80,

Location: app/Services/Collector/CollectorTokenHealthService.php, app/Console/Commands/EvaluateTokenHealth.php

Bonus Scraping (Plan 36)

Wenn alle taeglichen Pflicht-Scrapes abgearbeitet sind, nutzt das System die verbleibende Collector-Kapazitaet um Profile aus zukuenftigen Rotation-Buckets vorab zu scrapen.

Funktionsweise

  1. Idle-Detection: Queue-basiert — zaehlt offene Priority- und Low-Priority-Jobs. System gilt als idle wenn beide unter konfigurierbaren Schwellwerten liegen.
  2. Kandidaten-Auswahl: Profile aus zukuenftigen rotation_buckets, sortiert nach Bucket-Proximity und Follower-Count.
  3. Dispatch: Jobs mit job_type=bonus_scrape, priority=-10, low_priority=true — Duty-Jobs haben immer Vorrang.
  4. Metriken-Isolation: Bonus-Scrapes inkrementieren DailyScrapeProgress nicht, damit die regulaere Fortschrittsanzeige korrekt bleibt.

Tageslimit

Globaler Cache-Counter bonus_scrapes:{date} begrenzt die taegliche Anzahl (Default: max_daily_jobs in Config). Wird von Instagram und YouTube Bonus-Scraping geteilt.

YouTube Bonus

Eigener Command social:bonus-youtube-scrapes (alle 2h, 10-22 UTC). Prueft >20% Pool-Quota-Budget vor Dispatch. Re-prueft Quota alle 100 Profile.

Collector Exclusion

Bei transientem Fehler wird excluded_collector_id gesetzt — ein anderer Collector uebernimmt den Job. Nach dem 2. Retry wird die Exclusion aufgehoben um Starvation bei nur 1 aktivem Collector zu verhindern.

Config

// config/postbox.php → bonus_scraping
'enabled' => true,
'min_hour_utc' => 8,
'max_daily_jobs' => 500,
'idle_threshold_priority' => 5,
'idle_threshold_lowprio' => 20,
'job_priority' => -10,
'chunk_size' => 25,

Location: app/Services/Collector/BonusScrapeService.php, app/Console/Commands/QueueBonusYouTubeScrapes.php

Admin-Features

Platform Filter

Admins koennen die Collector Queue und Logs nach Plattform filtern (all, instagram, youtube).

Priority Boost

Einzelne Jobs koennen per Admin-UI in der Prioritaet hochgestuft werden: priority = max(queued priority) + 1. Der Job wird damit als naechstes geleased.

Rescrape Shortcut

Admins koennen in /admin/social-profiles direkt ein priorisiertes Rescrape ausloesen -- der zugehoerige Watcher wird in die Prioritaets-Queue gestellt.

Location: app/Livewire/Admin/LogQueue/Index.php, app/Livewire/Admin/SocialProfiles/Index.php