fix(usage): minimize session receipts and clarify privacy controls
This commit is contained in:
@@ -67,6 +67,31 @@ beforeEach(() => {
|
||||
});
|
||||
afterEach(cleanup);
|
||||
describe('product usage controls', () => {
|
||||
it('offers identity-free audit receipts after opt-out without restoring participation controls', async () => {
|
||||
vi.mocked(service.status).mockResolvedValue({
|
||||
...status,
|
||||
privacy_receipts: {
|
||||
last_deletion: {
|
||||
receipt_version: 'local-audit.v1',
|
||||
kind: 'deletion',
|
||||
status: 'collector-confirmed'
|
||||
}
|
||||
}
|
||||
});
|
||||
mount();
|
||||
expect(
|
||||
await screen.findByRole('button', { name: 'productUsage.auditDownload' })
|
||||
).toBeEnabled();
|
||||
expect(
|
||||
screen.getByText('productUsage.auditDescription')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByLabelText('productUsage.hash')
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('productUsage.feedbackTitle')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
it('requires the disclosure and an unchecked-by-default consent before enabling', async () => {
|
||||
mount();
|
||||
fireEvent.click(await screen.findByText('productUsage.review'));
|
||||
|
||||
@@ -191,13 +191,13 @@ export default function ProductUsageTab() {
|
||||
await queryClient.invalidateQueries({ queryKey: ['productUsage'] });
|
||||
}
|
||||
};
|
||||
const download = (value: unknown) => {
|
||||
const download = (value: unknown, filename = 'picpeak-usage-packets.json') => {
|
||||
const url = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(value, null, 2)], { type: 'application/json' })
|
||||
);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = 'picpeak-usage-packets.json';
|
||||
anchor.download = filename;
|
||||
anchor.click();
|
||||
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||
};
|
||||
@@ -300,6 +300,26 @@ export default function ProductUsageTab() {
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
{data.privacy_receipts &&
|
||||
Object.keys(data.privacy_receipts).length > 0 && (
|
||||
<Card padding="md" className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">
|
||||
{t('productUsage.auditTitle')}
|
||||
</h3>
|
||||
<p>{t('productUsage.auditDescription')}</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
download(
|
||||
data.privacy_receipts,
|
||||
'picpeak-usage-privacy-receipts.json'
|
||||
)
|
||||
}
|
||||
>
|
||||
{t('productUsage.auditDownload')}
|
||||
</Button>
|
||||
</Card>
|
||||
)}
|
||||
{active && (
|
||||
<>
|
||||
<Card padding="md" className="space-y-4">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"productUsage": {
|
||||
"auditTitle": "Export- und Löschquittungen",
|
||||
"auditDescription": "Lade deine privaten Nachweise herunter. PicPeak speichert nur die letzte Exportquittung während der Teilnahme und die letzte Löschbestätigung. Sie enthalten keinen Installationshash, Schlüssel oder Bericht-/Feedbackinhalt. Opt-out entfernt die lokale Exportquittung; die Löschbestätigung ohne Identitätsbezug bleibt erhalten. Der Collector führt keinen Export- oder Zugriffsverlauf.",
|
||||
"auditDownload": "Datenschutzquittungen herunterladen",
|
||||
"title": "Produktnutzung & Feedback",
|
||||
"noticeTitle": "Gestalten Sie PicPeak mit",
|
||||
"notice": "Optionale Nutzungsberichte zeigen, welche Funktionen für die Community wichtig sind. Die Übermittlung ist aus, bis Sie sich aktiv dafür entscheiden.",
|
||||
@@ -19,9 +22,9 @@
|
||||
"sectionFeedback": "Feedback ist getrennt",
|
||||
"fields": "Berichte enthalten einen Installationsfingerabdruck, PicPeak-Version, Berichtstag, Schema- und Signaturmetadaten, Galerie-Layouts sowie Konfiguriert/Genutzt-Werte für CRM und Unterfunktionen, Buchhaltung, Workflows, Newsletter, Gesichtserkennung, eigenes CSS, OAuth, SMTP, WhatsApp, Backups, S3 und eingebundene Freigaben. „Genutzt“ bedeutet seit der Teilnahme beobachtet, nicht wie häufig.",
|
||||
"excluded": "Automatische Berichte enthalten keine Galeriebesucher, Klickverläufe, Foto- oder Galerieanzahlen, Namen, E-Mail-Adressen, Domains, Dateinamen oder Zugangsdaten.",
|
||||
"transport": "Dein PicPeak-Backend verwahrt den Signaturschlüssel und sendet signierte Berichte an {{collector}}, einmal pro UTC-Tag bei Admin-Nutzung. Du kannst Berichte vorab ansehen und alle angenommenen Rohpakete herunterladen.",
|
||||
"visibility": "Der öffentliche Datensatz zeigt Funktionskombinationen und aggregierte Ergebnisse aller berichtenden Installationen, auch Gruppen mit nur einer Installation. Der Fingerabdruck ist pseudonym, nicht anonym. Bewahre deinen Abfrage-Hash vertraulich auf: Er ermöglicht lesenden Zugriff auf deine Rohpakete.",
|
||||
"deletion": "Deaktivieren stoppt die Erfassung sofort und fordert die Löschung deiner Berichte, Aggregatbeiträge, Rückmeldungen, veröffentlichten Wünsche/Empfehlungen, Stimmen und Sitzungen an. Ist der Dienst nicht erreichbar, bleiben nur die für die Löschung nötigen Zugangsdaten erhalten; die Oberfläche zeigt die ausstehende Löschung. Nach Bestätigung werden Hash und Schlüssel lokal gelöscht. Eine erneute Teilnahme erzeugt eine neue Identität. Der Dienst behält nur einen Einweg-Sperrwert, um wiederholte alte Registrierungen abzuweisen.",
|
||||
"transport": "Dein PicPeak-Backend verwahrt den Signaturschlüssel und sendet einmal pro UTC-Tag bei Admin-Nutzung signierte Nutzungsberichte an {{collector}}. Du kannst Berichte vorab ansehen und jeden eindeutig angenommenen Bericht genau wie beim ersten Empfang herunterladen; Übertragungswiederholungen werden zusammengeführt. Abgelehnte Versuche und getrennt gesendetes Feedback gehören nicht zu diesem Berichtsexport.",
|
||||
"visibility": "Nur teilnehmende Installationen können den Funktionsdatensatz und aggregierte Ergebnisse einsehen, auch Gruppen mit nur einer Installation. Schema und Quellcode sind öffentlich; geprüfte Funktionswünsche und Empfehlungen werden nur mit Erlaubnis ihrer Verfasser veröffentlicht. Dein Fingerabdruck ist pseudonym, nicht anonym. Bewahre deinen Abfrage-Hash vertraulich auf: Er ermöglicht lesenden Zugriff auf deine eigenen Berichte und den Teilnehmerdatensatz.",
|
||||
"deletion": "Deaktivieren stoppt die Erfassung sofort und fordert die Löschung der Berichte, Aggregatbeiträge, Rückmeldungen, Veröffentlichungen, Stimmen und Sitzungen an. Bei einem Ausfall bleiben nur die zur Löschung nötigen Zugangsdaten erhalten; die Oberfläche zeigt die ausstehende Löschung. Nach Bestätigung werden Hash und Schlüssel lokal gelöscht; eine erneute Teilnahme erzeugt eine neue Identität. Der Collector behält einen Einweg-Sperrwert und kurzlebige Missbrauchszähler ohne Installationsbezug. PicPeak speichert eine herunterladbare lokale Löschquittung ohne den alten Hash, Schlüssel oder Inhalte.",
|
||||
"feedbackDisclosure": "Feedback wird getrennt von automatischen Berichten und nur beim Absenden übertragen. Jeder Beitrag ist anonym, sofern du keinen Namen angibst, und nur für Betreuer sichtbar, sofern du die Veröffentlichung nicht ausdrücklich erlaubst. Öffentliche Beiträge werden geprüft. Die Verwendung einer Empfehlung für Marketing benötigt eine zusätzliche Erlaubnis.",
|
||||
"consentCheck": "Ich habe diese Hinweise gelesen und stimme der Teilnahme ausdrücklich zu.",
|
||||
"enable": "Produktnutzung aktivieren",
|
||||
@@ -36,8 +39,8 @@
|
||||
"signingKeyUnreadable": "Der Signaturschlüssel für die Nutzungsdaten kann nicht gelesen werden. Meist wurde USAGE_ENCRYPTION_KEY — oder das als Rückfallwert genutzte JWT_SECRET — geändert. Berichte können nicht gesendet und auch die Löschanfrage kann nicht signiert werden. Stellen Sie das ursprüngliche Schlüsselmaterial wieder her, um die Löschung abzuschließen; erneutes Senden oder Deaktivieren allein behebt dies nicht.",
|
||||
"inspect": "Genau sehen, was geteilt wird",
|
||||
"preview": "Nächsten Bericht ansehen",
|
||||
"lastPacket": "Zuletzt angenommenes signiertes Paket",
|
||||
"export": "Alle Rohpakete herunterladen",
|
||||
"lastPacket": "Zuletzt angenommener signierter Nutzungsbericht",
|
||||
"export": "Alle angenommenen Nutzungsberichte herunterladen",
|
||||
"connect": "Mit Wünschen & Abstimmungen verbinden",
|
||||
"openPortal": "Portal öffnen (Abstimmungssitzung für 15 Minuten)",
|
||||
"queued": "Der Vorgang ist zur erneuten Übertragung gespeichert. Der Empfang ist noch nicht bestätigt.",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"productUsage": {
|
||||
"auditTitle": "Export and deletion receipts",
|
||||
"auditDescription": "Download your private audit receipts. PicPeak keeps only the latest export receipt during participation and the latest deletion confirmation. These contain no installation hash, key or report/feedback content. Opt-out removes the local export receipt; the identity-free deletion confirmation remains. The collector does not keep an export/access history.",
|
||||
"auditDownload": "Download privacy receipts",
|
||||
"title": "Product usage & feedback",
|
||||
"noticeTitle": "Help shape PicPeak",
|
||||
"notice": "Optional product usage reports show which features matter to the community. Reporting is off until you choose to participate.",
|
||||
@@ -19,9 +22,9 @@
|
||||
"sectionFeedback": "Feedback is separate",
|
||||
"fields": "Reports contain an installation fingerprint, PicPeak version, report day, schema and signing metadata, gallery layout choices, and configured/used booleans for CRM and its subfeatures, accounting, workflows, newsletters, face recognition, custom CSS, OAuth, SMTP, WhatsApp, backups, S3, and share mounts. “Used” means observed since joining, not how often.",
|
||||
"excluded": "No gallery visitors, clickstreams, photo or gallery counts, names, emails, domains, filenames, or configuration secrets are included in automatic usage reports.",
|
||||
"transport": "Your PicPeak backend keeps the signing key and sends signed reports to {{collector}} once per UTC day when an admin uses the app. You can preview reports and download every accepted raw packet.",
|
||||
"visibility": "The public dataset shows feature combinations and aggregate results from all reporting installations, including groups containing just one installation. The fingerprint is pseudonymous, not anonymous. Keep your lookup hash private: it grants read-only access to your raw packets.",
|
||||
"deletion": "Disabling immediately stops collection and requests deletion of your remote reports, aggregate contributions, feedback, published requests/testimonials, votes, and sessions. If the collector is unavailable, only the credentials needed to finish deletion remain and the UI shows deletion pending. After confirmation, the local hash and key are erased. Joining again creates a new identity. The collector retains only a one-way revocation digest to prevent old registrations being replayed.",
|
||||
"transport": "Your PicPeak backend keeps the signing key and sends signed usage reports to {{collector}} once per UTC day during admin use. Preview reports and download each unique accepted report exactly as first received; transport retries are deduplicated. Rejected attempts and separately submitted feedback are not part of this report export.",
|
||||
"visibility": "Only participating installations can inspect the feature dataset and aggregate results, including groups of one. The schema and source are public; approved feature requests and testimonials are public only with their authors’ permission. Your fingerprint is pseudonymous, not anonymous. Keep your lookup hash private: it grants read-only access to your own reports and the participant dataset.",
|
||||
"deletion": "Disabling immediately stops collection and requests deletion of reports, aggregate contributions, feedback, published items, votes and sessions. During an outage, only credentials needed to finish deletion remain and the UI shows deletion pending. After confirmation, the local hash and key are erased; rejoining creates a new identity. The collector retains a one-way revocation digest and short-lived identity-free abuse counters. PicPeak keeps a downloadable local deletion receipt without the old hash, key or payloads.",
|
||||
"feedbackDisclosure": "Feedback is separate from automatic reports and is sent only when you submit it. Each item is anonymous unless you include a name, and private to maintainers unless you explicitly permit publication. Public items require maintainer review. Marketing use of a testimonial requires separate permission.",
|
||||
"consentCheck": "I have read this disclosure and explicitly agree to participate.",
|
||||
"enable": "Enable product usage",
|
||||
@@ -36,8 +39,8 @@
|
||||
"signingKeyUnreadable": "The usage signing key cannot be read, which usually means USAGE_ENCRYPTION_KEY — or JWT_SECRET, which it falls back to — was changed. Reports cannot be sent and the deletion request cannot be signed either. Restore the original encryption material to finish deletion; retrying or disabling will not resolve it on its own.",
|
||||
"inspect": "See exactly what is shared",
|
||||
"preview": "Preview next report",
|
||||
"lastPacket": "Last accepted signed packet",
|
||||
"export": "Download all raw packets",
|
||||
"lastPacket": "Last accepted signed usage report",
|
||||
"export": "Download all accepted usage reports",
|
||||
"connect": "Connect to requests & voting",
|
||||
"openPortal": "Open the portal (15-minute voting session)",
|
||||
"queued": "The operation is saved for retry. It has not been confirmed as delivered.",
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface UsageStatus {
|
||||
last_error: string | null;
|
||||
pending_action: string | null;
|
||||
last_packet: unknown;
|
||||
privacy_receipts?: Record<string, unknown>;
|
||||
feedback_preferences: { name: string };
|
||||
}
|
||||
export interface ProductFeedback {
|
||||
|
||||
Reference in New Issue
Block a user