fix(usage): introduce consented v4 without changing historical reports

This commit is contained in:
Paul Nothaft
2026-09-06 21:34:04 +02:00
parent 3cc893126d
commit ef8a52f02c
21 changed files with 5046 additions and 197 deletions
@@ -1,6 +1,6 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import catalog from './usageFeatures.v3.json';
import catalog from './usageFeatures.v4.json';
/** Local, static disclosure: opening it never contacts the collector. */
export function UsageCatalog() {
@@ -69,7 +69,7 @@ beforeEach(() => {
};
});
afterEach(cleanup);
it('shows every v2 signal locally before participation, without collector calls', async () => {
it('shows every v4 signal locally before participation, without collector calls', async () => {
mount();
await screen.findByText('productUsage.catalogTitle');
expect(screen.getAllByRole('heading', { level: 4, hidden: true })).toHaveLength(87);
@@ -77,8 +77,8 @@ it('shows every v2 signal locally before participation, without collector calls'
expect(service.preview).not.toHaveBeenCalled();
expect(service.upgradeConsent).not.toHaveBeenCalled();
});
it('existing v1 requires renewed unchecked consent; cancellation keeps v1 unchanged', async () => {
vi.mocked(service.status).mockResolvedValue({ ...status, status: 'active', consent_update_available: true });
it.each(['usage.v1', 'usage.v2', 'usage.v3'])('existing %s requires renewed unchecked consent; cancellation keeps its scope unchanged', async (schema_version) => {
vi.mocked(service.status).mockResolvedValue({ ...status, status: 'active', schema_version, consent_update_available: true });
vi.mocked(service.upgradeConsent).mockResolvedValue({ delivered: false, queued: true, state: { ...status, status: 'active', pending_action: 'consent' } });
mount();
fireEvent.click(await screen.findByText('productUsage.reviewUpgrade'));
@@ -1182,18 +1182,18 @@
},
"used": null
},
"gallery_downloads_restricted": {
"gallery_downloads": {
"category": "gallery_configuration",
"since": "usage.v3",
"since": "usage.v2",
"measurement": "configuration",
"configuration": "configuration",
"name": {
"en": "Gallery downloads restricted",
"de": "Galerie-Downloads eingeschränkt"
"en": "Gallery downloads allowed",
"de": "Galerie-Downloads erlaubt"
},
"configured": {
"en": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts.",
"de": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
"en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.",
"de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
},
"used": null
},
File diff suppressed because it is too large Load Diff
+12 -8
View File
@@ -1,17 +1,17 @@
{
"productUsage": {
"fields": "usage.v3-Berichte enthalten einen Installationsfingerabdruck, PicPeak-Version, UTC-Berichtsdatum und Erstellungszeit, Schema-/Signaturmetadaten, feste Galerie-Layouts, 86 Funktionssignale (63 Konfiguriert/Genutzt-Paare und 23 reine Konfigurationswerte) sowie zwei Gesamtzahlen der Installation: gespeicherte Galerien und Fotoeinträge ohne Videos. Entwürfe, archivierte Galerien und deren erhaltene Fotoeinträge zählen mit. Der Katalog unten erklärt jedes Feld. Keine Aktionsanzahlen oder Besucherbeobachtung.",
"catalogTitle": "Vollständiger Katalog: 86 Funktionssignale und 2 Bestandszahlen (usage.v3)",
"fields": "usage.v4-Berichte enthalten einen Installationsfingerabdruck, PicPeak-Version, UTC-Berichtsdatum und Erstellungszeit, Schema-/Signaturmetadaten, feste Galerie-Layouts, 86 Funktionssignale (63 Konfiguriert/Genutzt-Paare und 23 reine Konfigurationswerte) sowie zwei Gesamtzahlen der Installation: gespeicherte Galerien und Fotoeinträge ohne Videos. Entwürfe, archivierte Galerien und deren erhaltene Fotoeinträge zählen mit. Der Katalog unten erklärt jedes Feld. Keine Aktionsanzahlen oder Besucherbeobachtung.",
"catalogTitle": "Vollständiger Katalog: 86 Funktionssignale und 2 Bestandszahlen (usage.v4)",
"catalogExplanation": "Konfiguriert beschreibt die aktuelle technische Verfügbarkeit oder Einrichtung. Integriert bedeutet verfügbar, nicht genutzt. Genutzt ist ein installationsweites Ja/Nein seit Zustimmung zum Berichtsschema; angenommene Aufträge gelten als gestartet, nicht zwingend abgeschlossen. Reine Konfigurationssignale enthalten kein Genutzt-Feld. Der Bestand enthält nur aktuelle Gesamtzahlen der Galerie-/Fotoeinträge, ohne Aufschlüsselung nach Galerien. Nutzungsmarker speichern keine Personen, Objektkennungen, Aktionszeiten oder Häufigkeiten.",
"catalogSearch": "Funktionsname oder Schlüssel suchen",
"catalogEmpty": "Keine passenden Funktionen.",
"configuredLabel": "Konfiguriert",
"usedLabel": "Genutzt",
"configurationOnly": "Nur Konfiguration — tatsächliche Nutzung wird nicht erfasst.",
"versionDisclosure": "Diese Zustimmung gilt für usage.v3 / usage-consent.v3. Bestehende v1- und v2-Teilnahmen behalten ihre bisherigen 19 bzw. 73 Fähigkeiten ohne Bestandszahlen bis zur ausdrücklichen Erweiterung. Identität und Rohhistorie bleiben erhalten; lokale Nutzungsmarker beginnen erst nach Bestätigung durch den Collector neu. Höchstens ein Bericht pro UTC-Tag wird angenommen, der erste v3-Bericht kann daher am nächsten aktiven Tag folgen. Neue Marker und Bestandszahlen werden vor Bestätigung nicht erfasst.",
"versionDisclosure": "Diese Zustimmung gilt für usage.v4 / usage-consent.v4. Sie ersetzt die Frage „Erlaubt mindestens eine Galerie Downloads?“ durch „Hat mindestens eine Galerie Downloads abgeschaltet?“. Bestehende v1/v2/v3-Teilnahmen behalten ihren bisherigen Umfang bis zur ausdrücklichen Erweiterung. Identität und Rohhistorie bleiben erhalten; wartende Pakete werden vor dem Upgrade unverändert zugestellt. Lokale Nutzungsmarker beginnen erst nach Bestätigung neu. Der erste v4-Bericht kann am nächsten aktiven UTC-Tag folgen. Das neue Signal wird vor Bestätigung nicht erfasst.",
"currentSchema": "Aktuelles Berichtsschema: {{schema}}",
"reviewUpgrade": "Erweiterten Umfang von usage.v3 prüfen",
"upgrade": "usage.v3 ausdrücklich zustimmen",
"reviewUpgrade": "Erweiterten Umfang von usage.v4 prüfen",
"upgrade": "usage.v4 ausdrücklich zustimmen",
"upgradeExplanation": "Deine bestehende Teilnahme behält ihren bisherigen Umfang. Prüfe den erweiterten Katalog und die beiden Bestandszahlen, bevor du dich entscheidest. Ablehnen beendet die Teilnahme nicht.",
"upgradePending": "Die signierte Erweiterung wartet auf Bestätigung. Es wird nur der bisher bestätigte Umfang erfasst. Wiederhole den Versuch, wenn der Collector erreichbar ist, oder deaktiviere die Teilnahme zum Stoppen und Löschen.",
"catalog": {
@@ -335,9 +335,9 @@
"name": "Gast-Uploads aktiviert",
"configured": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
},
"gallery_downloads_restricted": {
"name": "Galerie-Downloads eingeschränkt",
"configured": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
"gallery_downloads": {
"name": "Galerie-Downloads erlaubt",
"configured": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
},
"download_resolution_picker": {
"name": "Download-Auflösungswahl aktiviert",
@@ -421,6 +421,10 @@
"download_original_filenames": {
"name": "Originaldateinamen für Downloads aktiviert",
"configured": "Der Schalter für Originaldateinamen beim Download ist aktiviert; keine Dateinamen oder Downloads werden gelesen oder gesendet."
},
"gallery_downloads_restricted": {
"name": "Galerie-Downloads eingeschränkt",
"configured": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen."
}
},
"auditTitle": "Export- und Löschquittungen",
+12 -8
View File
@@ -1,17 +1,17 @@
{
"productUsage": {
"fields": "usage.v3 reports contain an installation fingerprint, PicPeak version, UTC report date and generation time, schema/signing metadata, controlled gallery layouts, 86 fixed capability signals (63 configured/used pairs and 23 configuration-only booleans), and two installation totals: stored galleries and photo records excluding videos. Drafts and archived galleries and their retained photo records are included. The catalog below defines every field. There are no action counts or visitor observations.",
"catalogTitle": "Full catalog: 86 capability signals and 2 inventory totals (usage.v3)",
"fields": "usage.v4 reports contain an installation fingerprint, PicPeak version, UTC report date and generation time, schema/signing metadata, controlled gallery layouts, 86 fixed capability signals (63 configured/used pairs and 23 configuration-only booleans), and two installation totals: stored galleries and photo records excluding videos. Drafts and archived galleries and their retained photo records are included. The catalog below defines every field. There are no action counts or visitor observations.",
"catalogTitle": "Full catalog: 86 capability signals and 2 inventory totals (usage.v4)",
"catalogExplanation": "Configured describes current technical availability or configuration. Built-in means available, not used. Used is one installation-wide yes/no bit since consent to the reporting schema; accepted jobs mean initiated, not necessarily completed. Configuration-only capabilities omit used. Inventory contains only current gallery/photo record totals, with no per-gallery breakdown. No actor, entity identifier, action time or frequency is stored in usage markers.",
"catalogSearch": "Search capability name or key",
"catalogEmpty": "No matching capabilities.",
"configuredLabel": "Configured",
"usedLabel": "Used",
"configurationOnly": "Configuration only — actual use is not collected.",
"versionDisclosure": "This consent covers usage.v3 / usage-consent.v3. Existing v1 and v2 participants retain their previous 19 or 73 capabilities without inventory totals until they explicitly upgrade. Identity and raw history remain; local usage markers restart only after the collector confirms the upgrade. At most one report per UTC day is accepted, so the first v3 report may be on the next active day. New markers and totals are not collected before confirmation.",
"versionDisclosure": "This consent covers usage.v4 / usage-consent.v4. It replaces the question “does any gallery allow downloads?” with “does any gallery have downloads switched off?”. Existing v1/v2/v3 participants keep their exact previous scope until they explicitly upgrade. Identity and raw history remain; pending packets are delivered unchanged before an upgrade. Local usage markers restart only after confirmation. The first v4 report may be on the next active UTC day. The new signal is never collected before confirmation.",
"currentSchema": "Current reporting schema: {{schema}}",
"reviewUpgrade": "Review expanded usage.v3 scope",
"upgrade": "Explicitly agree to usage.v3",
"reviewUpgrade": "Review expanded usage.v4 scope",
"upgrade": "Explicitly agree to usage.v4",
"upgradeExplanation": "Your existing participation keeps its current scope. Review the expanded catalog and the two inventory totals before deciding whether to upgrade. Declining does not end participation.",
"upgradePending": "The signed consent upgrade is pending confirmation. Only the previously accepted scope is collected. Retry when the collector is available, or disable participation to stop and delete.",
"catalog": {
@@ -335,9 +335,9 @@
"name": "Guest uploads enabled",
"configured": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts."
},
"gallery_downloads_restricted": {
"name": "Gallery downloads restricted",
"configured": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts."
"gallery_downloads": {
"name": "Gallery downloads allowed",
"configured": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts."
},
"download_resolution_picker": {
"name": "Download resolution picker enabled",
@@ -421,6 +421,10 @@
"download_original_filenames": {
"name": "Original download filenames enabled",
"configured": "The original-download-filenames switch is enabled; no filenames or downloads are read or sent."
},
"gallery_downloads_restricted": {
"name": "Gallery downloads restricted",
"configured": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts."
}
},
"auditTitle": "Export and deletion receipts",
@@ -49,12 +49,12 @@ export const productUsageService = {
async enable(): Promise<UsageStatus> {
return (
await api.post('/admin/usage/enable', {
consent_version: 'usage-consent.v3'
consent_version: 'usage-consent.v4'
})
).data;
},
async upgradeConsent(): Promise<{ delivered: boolean; queued: boolean; state: UsageStatus }> {
return (await api.post('/admin/usage/consent', { consent_version: 'usage-consent.v3' })).data;
return (await api.post('/admin/usage/consent', { consent_version: 'usage-consent.v4' })).data;
},
async disable(): Promise<UsageStatus> {
return (await api.post('/admin/usage/disable')).data;