integrated umami

This commit is contained in:
t.indorf
2026-05-11 09:28:06 +02:00
parent 639c470cb2
commit fcea4a9168
6 changed files with 90 additions and 5 deletions
+6
View File
@@ -14,7 +14,13 @@ RUN npm ci
FROM base AS builder
ARG NEXT_PUBLIC_SITE_URL
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_DOMAINS
ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
ENV NEXT_PUBLIC_UMAMI_DOMAINS=$NEXT_PUBLIC_UMAMI_DOMAINS
ENV DATABASE_URL=postgresql://build:build@localhost:5432/build?schema=public
COPY --from=deps /app/node_modules ./node_modules
+6
View File
@@ -21,6 +21,12 @@ AUTH_URL="https://[deine-domain.de]"
NEXTAUTH_URL="https://[deine-domain.de]"
NEXT_PUBLIC_SITE_URL="https://[deine-domain.de]"
# Umami Analytics (optional, cookieless)
# In Umami unter Website -> Tracking code auslesen.
NEXT_PUBLIC_UMAMI_SCRIPT_URL="https://analytics.[deine-domain.de]/script.js"
NEXT_PUBLIC_UMAMI_WEBSITE_ID="[UMAMI_WEBSITE_ID]"
NEXT_PUBLIC_UMAMI_DOMAINS="[deine-domain.de],www.[deine-domain.de]"
# Mailversand via Resend
RESEND_API_KEY="re_[PRODUCTION_API_KEY]"
EMAIL_FROM="Kita-Planer <[absender@deine-domain.de]>"
+6
View File
@@ -5,6 +5,9 @@ services:
dockerfile: Dockerfile
args:
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:?}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL:-}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID:-}
NEXT_PUBLIC_UMAMI_DOMAINS: ${NEXT_PUBLIC_UMAMI_DOMAINS:-}
environment:
NODE_ENV: production
PORT: 3000
@@ -14,6 +17,9 @@ services:
AUTH_URL: ${AUTH_URL:?}
NEXTAUTH_URL: ${NEXTAUTH_URL:?}
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:?}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL:-}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID:-}
NEXT_PUBLIC_UMAMI_DOMAINS: ${NEXT_PUBLIC_UMAMI_DOMAINS:-}
RESEND_API_KEY: ${RESEND_API_KEY:?}
EMAIL_FROM: ${EMAIL_FROM:?}
ADMIN_EMAIL: ${ADMIN_EMAIL:?}
+22 -5
View File
@@ -180,14 +180,31 @@ export default function DatenschutzPage() {
<p className="mt-3 leading-7 text-slate-700">
Kita-Planer verwendet technisch notwendige Cookies oder
vergleichbare Technologien, um Login-Sitzungen sicher
bereitzustellen. [Falls Analyse- oder Marketing-Tools eingesetzt
werden, hier ergänzen.]
bereitzustellen. Für die öffentliche Website kann zusätzlich
Umami Analytics eingesetzt werden, um Seitenaufrufe anonymisiert
und ohne Cookies auszuwerten.
</p>
</div>
<div>
<h2 className="text-xl font-semibold tracking-tight">
8. Rechte betroffener Personen
8. Webanalyse mit Umami
</h2>
<p className="mt-3 leading-7 text-slate-700">
Wir nutzen [Umami-Instanz / Betreiber eintragen], um die Nutzung
der öffentlichen Website statistisch auszuwerten. Umami verwendet
keine Cookies, respektiert die Do-Not-Track-Einstellung des
Browsers und erfasst keine Analyseaufrufe im geschützten
Dashboard. Erfasst werden insbesondere Seitenaufrufe,
Referrer-Informationen, Browser-, Geräte- und Länderinformationen
in aggregierter Form. Die Rechtsgrundlage ist [Rechtsgrundlage
prüfen und eintragen].
</p>
</div>
<div>
<h2 className="text-xl font-semibold tracking-tight">
9. Rechte betroffener Personen
</h2>
<p className="mt-3 leading-7 text-slate-700">
Betroffene Personen haben nach Maßgabe der DSGVO Rechte auf
@@ -199,7 +216,7 @@ export default function DatenschutzPage() {
<div>
<h2 className="text-xl font-semibold tracking-tight">
9. Beschwerderecht
10. Beschwerderecht
</h2>
<p className="mt-3 leading-7 text-slate-700">
Betroffene Personen können sich bei einer
@@ -211,7 +228,7 @@ export default function DatenschutzPage() {
<div>
<h2 className="text-xl font-semibold tracking-tight">
10. Aktualität dieser Datenschutzerklärung
11. Aktualität dieser Datenschutzerklärung
</h2>
<p className="mt-3 leading-7 text-slate-700">
Stand: [Datum]. Wir behalten uns vor, diese Datenschutzerklärung
+2
View File
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Toaster } from "@/components/ui/sonner";
import { UmamiAnalytics } from "@/components/analytics/umami-analytics";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -59,6 +60,7 @@ export default function RootLayout({
>
<body className="min-h-full flex flex-col">
{children}
<UmamiAnalytics />
<Toaster />
</body>
</html>
@@ -0,0 +1,48 @@
import Script from "next/script";
const publicAnalyticsPaths = new Set([
"/",
"/impressum",
"/datenschutz",
"/login",
"/register",
]);
export function UmamiAnalytics() {
const scriptUrl = process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL;
const websiteId = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
const domains = process.env.NEXT_PUBLIC_UMAMI_DOMAINS;
if (!scriptUrl || !websiteId) {
return null;
}
return (
<>
<Script id="umami-before-send" strategy="afterInteractive">
{`
window.kitaPlanerUmamiBeforeSend = function (_type, payload) {
try {
var url = new URL(payload && payload.url ? payload.url : window.location.href, window.location.origin);
var path = url.pathname.length > 1 && url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
var allowedPaths = ${JSON.stringify(Array.from(publicAnalyticsPaths))};
return allowedPaths.indexOf(path) === -1 ? false : payload;
} catch (_error) {
return false;
}
};
`}
</Script>
<Script
src={scriptUrl}
strategy="afterInteractive"
data-website-id={websiteId}
data-domains={domains}
data-exclude-search="true"
data-exclude-hash="true"
data-do-not-track="true"
data-before-send="kitaPlanerUmamiBeforeSend"
/>
</>
);
}