TeamVis Self-Host-Bundle v0.56.2

This commit is contained in:
TeamVis Release committed 2026-10-10 23:39:23 +02:00
1 parent f53579d3a4
commit 6da3cc2283
11 files changed
+432 -30

No files matched your search

@@ -0,0 +1,103 @@
-- 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$;
+29
View File
@@ -0,0 +1,29 @@
-- ====================================================================
-- 0068_dark_theme — dunkle Darstellung je Mandant (Portal und Karte)
-- ====================================================================
-- Drei optionale Farben. Leer = aus der hellen Palette abgeleitet
-- (lib/branding.ts, darkTheme()):
-- dark_bg Seitengrund Default: Sekundär tief
-- dark_surface Karten, Felder Default: Sekundär dunkel
-- dark_accent Schrift, Links, Fokus, Linien in Markenfarbe
-- Default: Primärfarbe aufgehellt bis 4,5:1 auf dark_surface
-- Die Primärfarbe selbst bleibt im Dunkeln reine Fläche (Buttons, Kopf) —
-- auf dunklem Grund reicht ihr Kontrast als Schrift in der Regel nicht.
--
-- Nur additiv. Anon-Lesezugriff ausschließlich auf die drei neuen Spalten
-- (die öffentliche Karte liest site_settings über den anon-Client); die
-- bestehenden Spalten-Grants bleiben unberührt.
alter table public.site_settings
add column if not exists dark_bg text,
add column if not exists dark_surface text,
add column if not exists dark_accent text;
comment on column public.site_settings.dark_bg is
'Dunkle Darstellung: Seitengrund (Hex). Leer = Sekundär tief.';
comment on column public.site_settings.dark_surface is
'Dunkle Darstellung: Karten und Felder (Hex). Leer = Sekundär dunkel.';
comment on column public.site_settings.dark_accent is
'Dunkle Darstellung: Markenfarbe für Schrift, Links und Linien (Hex). Leer = aufgehellte Primärfarbe.';
grant select (dark_bg, dark_surface, dark_accent) on public.site_settings to anon;
@@ -0,0 +1,21 @@
-- ====================================================================
-- 0069_theme_default — Standard-Darstellung je Mandant
-- ====================================================================
-- Was Portal und Karte zeigen, solange jemand im Portal unter „Darstellung"
-- nichts gewählt hat: 'light' (Vorgabe), 'dark' oder 'system' (folgt der
-- Geräteeinstellung). Eine eigene Wahl (Cookie tv_theme) geht immer vor.
-- Additiv; anon-Lesezugriff nur auf diese Spalte (die Karte liest anon).
alter table public.site_settings
add column if not exists theme_default text not null default 'light';
alter table public.site_settings
drop constraint if exists site_settings_theme_default_check;
alter table public.site_settings
add constraint site_settings_theme_default_check
check (theme_default in ('light', 'dark', 'system'));
comment on column public.site_settings.theme_default is
'Standard-Darstellung von Portal und Karte: light | dark | system.';
grant select (theme_default) on public.site_settings to anon;
+17
View File
@@ -0,0 +1,17 @@
-- ====================================================================
-- 0070_dark_brand — eigene Markenfläche für die dunkle Darstellung
-- ====================================================================
-- Die Primärfarbe trägt auf dunklem Grund oft nicht als Fläche (Stadtwerk:
-- #931041 wirkt auf #11181f fast schwarz). dark_brand ist die Farbe für
-- Buttons, Badges und aktive Markierungen im Dunkeln, immer mit weißer
-- Schrift. Leer = Primärfarbe. Stadtwerk-Festlegung (überarbeitet
-- 26.09.2026): #a5204d.
-- Additiv; anon-Lesezugriff nur auf diese Spalte.
alter table public.site_settings
add column if not exists dark_brand text;
comment on column public.site_settings.dark_brand is
'Dunkle Darstellung: Markenfläche für Buttons und Badges (Hex, weiße Schrift). Leer = Primärfarbe.';
grant select (dark_brand) on public.site_settings to anon;