analytics: config-driven Umami tag + accurate privacy disclosure; fix @types/react v18/v19 dup that broke the build at HEAD

This commit is contained in:
jwinterm
2026-06-12 13:24:54 -04:00
parent e3cc38bded
commit 468eaa1d60
5 changed files with 49 additions and 14 deletions
+2
View File
@@ -0,0 +1,2 @@
NEXT_PUBLIC_UMAMI_HOST=https://stats.suchshop.lol
NEXT_PUBLIC_UMAMI_WEBSITE_ID=62a3259a-8293-4e11-901c-d7fb86d30847
+2
View File
@@ -4,6 +4,7 @@ import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";
import { DynamicFavicon } from "@/components/dynamic-favicon";
import { Footer } from "@/components/layout/footer";
import { UmamiAnalytics } from "@/components/UmamiAnalytics";
const inter = Inter({ subsets: ["latin"] });
@@ -119,6 +120,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<DynamicFavicon />
<UmamiAnalytics />
<div className="relative z-10 flex flex-col min-h-screen">
{children}
<Footer />
+21 -13
View File
@@ -22,7 +22,7 @@ export default function PrivacyPage() {
<h1 className="text-4xl font-bold mb-8">Privacy Policy</h1>
<div className="prose dark:prose-invert max-w-none space-y-6">
<p className="text-muted-foreground">Last updated: April 9, 2026</p>
<p className="text-muted-foreground">Last updated: June 12, 2026</p>
<section>
<h2 className="text-2xl font-semibold mt-8 mb-4">1. Who We Are</h2>
@@ -51,17 +51,25 @@ export default function PrivacyPage() {
information with third parties.
</p>
<h3 className="text-xl font-medium mt-4 mb-2">Automatically Collected Information</h3>
<h3 className="text-xl font-medium mt-4 mb-2">Analytics</h3>
<p>
Our Site does not use analytics services, tracking pixels, or advertising cookies.
We do not use Google Analytics, Facebook Pixel, or similar tracking tools on this website.
We use <strong>Umami</strong>, a privacy-respecting, self-hosted web analytics tool, to understand
aggregate traffic to our Site (such as which pages are visited and where visitors arrive from).
Umami is <strong>cookieless</strong> and runs entirely on our own infrastructure &mdash; your data is
never sent to a third-party analytics provider or data broker. It does <strong>not</strong> store your
IP address, set tracking cookies, build cross-site profiles, or collect personally identifiable
information. We honor the <strong>Do Not Track</strong> and <strong>Global Privacy Control</strong>
browser signals: if either is enabled, no analytics are collected from your visit.
</p>
<p>
Our hosting infrastructure (Vercel) may collect standard server logs including IP addresses, browser type,
and pages visited. This data is used for security and performance purposes and is subject to{" "}
<a href="https://vercel.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">
Vercel&apos;s Privacy Policy
</a>.
We do not use Google Analytics, Facebook Pixel, advertising cookies, or similar third-party tracking tools.
</p>
<h3 className="text-xl font-medium mt-4 mb-2">Server Logs</h3>
<p>
Our self-hosted infrastructure may record standard server logs (including IP addresses, browser type,
and pages visited) for security and performance purposes. These logs are not used to identify or
profile individual visitors and are retained only as long as needed for operational security.
</p>
<h3 className="text-xl font-medium mt-4 mb-2">Cookies and Local Storage</h3>
@@ -92,15 +100,15 @@ export default function PrivacyPage() {
<h2 className="text-2xl font-semibold mt-8 mb-4">4. Third-Party Services</h2>
<p>The Site uses the following third-party services:</p>
<ul className="list-disc pl-6 space-y-2">
<li>
<strong>Vercel</strong> — hosting and deployment.{" "}
<a href="https://vercel.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Privacy Policy</a>
</li>
<li>
<strong>Cloudflare Turnstile</strong> — bot protection on the contact form.{" "}
<a href="https://www.cloudflare.com/privacypolicy/" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Privacy Policy</a>
</li>
</ul>
<p className="mt-4">
Our website analytics (Umami) and hosting are self-hosted on our own infrastructure and are therefore
not third-party services.
</p>
</section>
<section>
@@ -0,0 +1,23 @@
'use client';
import { useEffect } from 'react';
// Self-hosted, cookieless Umami — config via env so nothing is hardcoded.
// Honors Global Privacy Control + Do-Not-Track. No-op unless both env vars are set.
const HOST = process.env.NEXT_PUBLIC_UMAMI_HOST;
const WEBSITE_ID = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
export function UmamiAnalytics() {
useEffect(() => {
if (!HOST || !WEBSITE_ID) return;
const nav = navigator as { globalPrivacyControl?: boolean };
if (nav.globalPrivacyControl) return;
if (document.querySelector(`script[data-website-id="${WEBSITE_ID}"]`)) return;
const s = document.createElement('script');
s.defer = true;
s.src = `${HOST}/script.js`;
s.setAttribute('data-website-id', WEBSITE_ID);
s.setAttribute('data-do-not-track', 'true');
document.head.appendChild(s);
}, []);
return null;
}