Add privacy-friendly analytics and cookie consent, integrated into the CMS

Adds a first-party analytics system with no third-party trackers and no
persistent per-visitor identifiers: events are grouped into sessions via a
daily-rotating pseudonymous hash (IP+UA+host, HMAC-salted, never stored raw).

- New "Analytics" section in the Payload admin, gated by a canViewAnalytics
  permission on Users, following the existing custom-admin-view pattern
  used by the calendar feature
- Sessions, page views, unique visitors, top/entry/exit pages, events,
  device/browser breakdown, date range filter with period comparison
- Raw events and daily aggregate rollups as their own collections, with
  configurable retention and a cron endpoint that rolls up before purging
- Cookie consent banner (necessary/preferences/statistics) with categories
  and copy editable from a new CMS global; a track() helper that is a
  complete no-op without statistics consent, so nothing is ever sent
  before a visitor opts in
- track() calls wired into the booking flow, contact form, and navigation
This commit is contained in:
2026-09-06 21:59:59 +02:00
parent 82c4261a81
commit 65b6eeac51
30 changed files with 2274 additions and 7 deletions
+29 -2
View File
@@ -16,6 +16,8 @@ import { Availability } from "@/collections/Availability";
import { AvailabilityOverrides } from "@/collections/AvailabilityOverrides";
import { BookingRequests } from "@/collections/BookingRequests";
import { Redirects } from "@/collections/Redirects";
import { AnalyticsEvents } from "@/collections/AnalyticsEvents";
import { AnalyticsDailyStats } from "@/collections/AnalyticsDailyStats";
import { Home } from "@/globals/Home";
import { About } from "@/globals/About";
@@ -26,6 +28,8 @@ import { Booking } from "@/globals/Booking";
import { MeetingSettings } from "@/globals/MeetingSettings";
import { BookingSettings } from "@/globals/BookingSettings";
import { SEOSettings } from "@/globals/SEOSettings";
import { CookieSettings } from "@/globals/CookieSettings";
import { AnalyticsSettings } from "@/globals/AnalyticsSettings";
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
@@ -41,7 +45,10 @@ export default buildConfig({
titleSuffix: "— Anouma Admin",
},
components: {
afterNavLinks: ["/components/admin/CalendarNavLink#CalendarNavLink"],
afterNavLinks: [
"/components/admin/CalendarNavLink#CalendarNavLink",
"/components/admin/AnalyticsNavLink#AnalyticsNavLink",
],
views: {
kalender: {
Component: "/components/admin/AdminCalendarView#AdminCalendarView",
@@ -49,6 +56,12 @@ export default buildConfig({
exact: true,
meta: { title: "Kalender" },
},
analytics: {
Component: "/components/admin/AdminAnalyticsView#AdminAnalyticsView",
path: "/analytics",
exact: true,
meta: { title: "Analytics" },
},
},
},
},
@@ -64,8 +77,22 @@ export default buildConfig({
AvailabilityOverrides,
BookingRequests,
Redirects,
AnalyticsEvents,
AnalyticsDailyStats,
],
globals: [
Home,
About,
AngeboteIntro,
AktuellesIntro,
Contact,
Booking,
MeetingSettings,
BookingSettings,
SEOSettings,
CookieSettings,
AnalyticsSettings,
],
globals: [Home, About, AngeboteIntro, AktuellesIntro, Contact, Booking, MeetingSettings, BookingSettings, SEOSettings],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || "",
typescript: {