Files
deklarix/internal/web/insights_reminder.go
noroot 6fd7831784 feat: Erinnerung zum Sichern von Story-Insights vor Ablauf
Ausgangspunkt: Instagram hält Story-Insights nach eigener Aussage nur
24 Stunden vor, auch der offizielle Datenexport enthält sie nicht mehr
danach. Der Standbild-Screenshot beim Prüfen entsteht direkt beim
Veröffentlichen, bevor nennenswerte Kennzahlen existieren — er kann
das strukturell nicht auffangen. Eine OAuth-Anbindung allein löst das
auch nicht: selbst mit API-Zugriff bräuchte es einen Abruf innerhalb
desselben 24h-Fensters.

- Migration 0007: asset.purpose ('initial' | 'insights', Default
  'initial' erhält die Bedeutung aller Bestandszeilen). Ein Beitrag
  kann jetzt mehrere Insights-Nachweise über die Zeit bekommen.
  GetLatestAssetForSubmission berücksichtigt weiterhin nur 'initial',
  damit ein späterer Insights-Upload nie den beim Archivieren
  referenzierten Original-Screenshot verdrängt.
- internal/web/insights_reminder.go: computeInsightsReminder — reine,
  ungetestete gegen echte Instagram-Daten, aber isoliert testbare
  Logik fürs Erinnerungs-Timing (Produktentscheidung, keine Rechtsnorm,
  daher nicht in rules/*.yaml).
- GET /beitraege/{id} zeigt die Erinnerung bei veröffentlichten
  "story"-Beiträgen ohne existierendes insights-Asset; POST
  /beitraege/{id}/insights speichert einen weiteren Screenshot (gleiche
  Validierung wie das initiale Standbild, wiederverwendet über
  readUploadedAsset/storeAsset mit purpose-Parameter).
- Bewusst nur In-App-Banner in dieser Ausbaustufe, kein Mail-/Push-
  Versand — dafür fehlt aktuell ein SMTP-Relay/Versanddienst, siehe
  CLAUDE.md-Hinweis dazu.

Volle Testsuite inkl. echter Postgres-Tests grün; End-to-End gegen
einen laufenden Server verifiziert (Story archivieren → Erinnerung
sichtbar → Insights-Upload → Erinnerung verschwindet, Nachweis
gelistet, Mandantentrennung beim Upload durchgesetzt).
2026-08-28 10:10:44 +02:00

48 lines
1.9 KiB
Go

package web
import (
"fmt"
"time"
)
// storyInsightsWindow ist das Zeitfenster, in dem Instagram nach
// eigener Aussage Story-Insights überhaupt vorhält — danach sind sie
// auch über den offiziellen Datenexport nicht mehr zu bekommen. Reine
// Produktentscheidung (Erinnerungs-Timing), keine Rechtsnorm — bewusst
// hier als Konstante und nicht in rules/*.yaml, das ist ausschließlich
// für die Kennzeichnungsprüfung reserviert (siehe CLAUDE.md).
const storyInsightsWindow = 24 * time.Hour
// insightsReminder beschreibt, ob und wie dringend eine Erinnerung
// angezeigt werden soll, die Kennzahlen (Insights) eines veröffentlichten
// Beitrags per Screenshot zu sichern, bevor sie unwiederbringlich
// verschwinden.
type insightsReminder struct {
Show bool
Urgent bool // Fenster läuft noch
Expired bool // Fenster ist wahrscheinlich schon vorbei
Message string
}
// computeInsightsReminder berechnet den Erinnerungsstatus. Nur für
// "story"-Beiträge relevant (siehe storyInsightsWindow); alle anderen
// Beitragstypen bekommen keine Erinnerung, da ihre Kennzahlen nicht auf
// dieselbe Art flüchtig sind.
func computeInsightsReminder(postType string, publishedAt time.Time, now time.Time, hasInsightsAsset bool) insightsReminder {
if postType != "story" || hasInsightsAsset || publishedAt.IsZero() {
return insightsReminder{}
}
elapsed := now.Sub(publishedAt)
if elapsed >= storyInsightsWindow {
return insightsReminder{
Show: true, Expired: true,
Message: "Das Zeitfenster für Story-Insights ist bei Instagram nach eigener Aussage abgelaufen (24 Stunden) — die Kennzahlen sind wahrscheinlich nicht mehr abrufbar.",
}
}
remainingHours := int((storyInsightsWindow - elapsed).Hours()) + 1
return insightsReminder{
Show: true, Urgent: true,
Message: fmt.Sprintf("Noch ca. %d Stunde(n), um die Story-Insights zu sichern, bevor sie bei Instagram verschwinden.", remainingHours),
}
}