Files
anouma/payload.config.ts
T
maro 65b6eeac51 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
2026-09-06 21:59:59 +02:00

115 lines
3.4 KiB
TypeScript

import { postgresAdapter } from "@payloadcms/db-postgres";
import { lexicalEditor } from "@payloadcms/richtext-lexical";
import path from "path";
import { buildConfig } from "payload";
import { fileURLToPath } from "url";
import sharp from "sharp";
import { Users } from "@/collections/Users";
import { Media } from "@/collections/Media";
import { Events } from "@/collections/Events";
import { Posts } from "@/collections/Posts";
import { Offers } from "@/collections/Offers";
import { EventRegistrations } from "@/collections/EventRegistrations";
import { Customers } from "@/collections/Customers";
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";
import { AngeboteIntro } from "@/globals/AngeboteIntro";
import { AktuellesIntro } from "@/globals/AktuellesIntro";
import { Contact } from "@/globals/Contact";
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);
export default buildConfig({
serverURL: process.env.NEXT_PUBLIC_SERVER_URL || "http://localhost:3000",
admin: {
user: Users.slug,
importMap: {
baseDir: path.resolve(dirname),
},
meta: {
titleSuffix: "— Anouma Admin",
},
components: {
afterNavLinks: [
"/components/admin/CalendarNavLink#CalendarNavLink",
"/components/admin/AnalyticsNavLink#AnalyticsNavLink",
],
views: {
kalender: {
Component: "/components/admin/AdminCalendarView#AdminCalendarView",
path: "/kalender",
exact: true,
meta: { title: "Kalender" },
},
analytics: {
Component: "/components/admin/AdminAnalyticsView#AdminAnalyticsView",
path: "/analytics",
exact: true,
meta: { title: "Analytics" },
},
},
},
},
collections: [
Users,
Customers,
Media,
Events,
Posts,
Offers,
EventRegistrations,
Availability,
AvailabilityOverrides,
BookingRequests,
Redirects,
AnalyticsEvents,
AnalyticsDailyStats,
],
globals: [
Home,
About,
AngeboteIntro,
AktuellesIntro,
Contact,
Booking,
MeetingSettings,
BookingSettings,
SEOSettings,
CookieSettings,
AnalyticsSettings,
],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || "",
typescript: {
outputFile: path.resolve(dirname, "payload-types.ts"),
},
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI || "",
},
}),
sharp,
// Server-side upload size limit; the Media collection restricts formats.
upload: {
limits: {
fileSize: 10 * 1024 * 1024, // 10 MB
},
},
plugins: [],
});