-- Karten-Analytics: Aufbewahrungsfrist und namentliche Auswertung -- ==================================================================== -- Zwei Datenschutz-Lücken aus 0017 werden hier geschlossen: -- -- 1. Die dort angekündigte Aufbewahrung von 365 Tagen war nur ein -- Kommentar — es gab keine Löschroutine, die Protokolle wuchsen -- unbegrenzt. Jetzt: konfigurierbare Frist (Default 90 Tage) plus -- `purge_card_view_log()`, die der Anwendungscode regelmäßig ruft -- und die zusätzlich per pg_cron laufen kann. -- -- 2. Die namentliche Auswertung je Beschäftigtem (Rangliste unter -- /admin/analytics) war ohne Schalter immer aktiv. Sie ist ab -- jetzt ein bewusstes Opt-in, Default AUS — auch für -- Bestandsinstanzen. Wo eine Belegschaft vertreten ist, ist die -- Auswertung mitbestimmungspflichtig (technische Einrichtung, -- geeignet zur Verhaltens- und Leistungskontrolle); sie darf -- deshalb nicht stillschweigend voreingestellt sein. alter table public.site_settings add column if not exists analytics_employee_breakdown boolean not null default false, add column if not exists analytics_retention_days integer not null default 90; comment on column public.site_settings.analytics_employee_breakdown is 'Namentliche Auswertung der Kartenaufrufe je Beschäftigtem im Admin-Backend. Opt-in, mitbestimmungspflichtig.'; comment on column public.site_settings.analytics_retention_days is 'Aufbewahrungsfrist für card_view_log in Tagen. Ältere Zeilen werden gelöscht.'; -- Plausibilitätsgrenzen: mindestens eine Woche (sonst ist die Statistik -- sinnlos), höchstens zwei Jahre (darüber ist die Erforderlichkeit für -- eine reine Aufrufzählung nicht mehr begründbar). alter table public.site_settings drop constraint if exists site_settings_analytics_retention_days_check; alter table public.site_settings add constraint site_settings_analytics_retention_days_check check (analytics_retention_days between 7 and 730); -- -------------------------------------------------------------------- -- Löschroutine -- -------------------------------------------------------------------- -- Ohne Argument gilt die in site_settings hinterlegte Frist. Rückgabe -- ist die Anzahl gelöschter Zeilen, damit ein Cron-Lauf etwas zu -- protokollieren hat. create or replace function public.purge_card_view_log(retention_days integer default null) returns integer language plpgsql security definer set search_path = public as $fn$ declare days integer; removed integer; begin days := coalesce( retention_days, (select s.analytics_retention_days from public.site_settings s where s.singleton limit 1), 90 ); delete from public.card_view_log where occurred_at < now() - make_interval(days => days); get diagnostics removed = row_count; return removed; end; $fn$; comment on function public.purge_card_view_log(integer) is 'Löscht Einträge aus card_view_log, die älter als die Aufbewahrungsfrist sind. Ohne Argument gilt site_settings.analytics_retention_days.'; -- Die Funktion fasst personenbeziehbare Protokolle an — kein Zugriff -- für anon/authenticated. revoke all on function public.purge_card_view_log(integer) from public; revoke all on function public.purge_card_view_log(integer) from anon; revoke all on function public.purge_card_view_log(integer) from authenticated; grant execute on function public.purge_card_view_log(integer) to service_role; -- -------------------------------------------------------------------- -- Zeitplan, sofern pg_cron vorhanden ist -- -------------------------------------------------------------------- -- Die Anwendung löscht ohnehin selbst (siehe lib/analytics.ts), das hier -- ist der saubere Weg für Instanzen mit pg_cron: die Frist greift dann -- auch, wenn die Karten längere Zeit nicht aufgerufen werden. Fehlt die -- Extension oder fehlen die Rechte, bleibt es beim App-seitigen Lauf — -- die Migration darf daran nicht scheitern. do $cron$ begin if exists (select 1 from pg_extension where extname = 'pg_cron') then begin perform cron.unschedule('teamvis-purge-card-view-log'); exception when others then null; end; perform cron.schedule( 'teamvis-purge-card-view-log', '30 3 * * *', 'select public.purge_card_view_log();' ); end if; exception when others then raise notice 'pg_cron-Zeitplan nicht eingerichtet (%). Die Anwendung löscht selbst.', sqlerrm; end; $cron$;