Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 3s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
@mintel/observability
Standardized observability package for the Mintel ecosystem, providing Umami analytics and Sentry/GlitchTip error tracking with a focus on privacy and ad-blocker resilience.
Features
- Umami Smart Proxy: Track analytics without external scripts and hide your Website ID.
- Sentry Relay: Bypass ad-blockers for error tracking by relaying envelopes through your own server.
- Unified API: consistent interface for tracking across multiple projects.
Installation
pnpm add @mintel/observability @sentry/nextjs
Usage
1. Unified Environment (via @mintel/next-utils)
Define the following environment variables:
# Analytics
UMAMI_WEBSITE_ID=your-website-id
UMAMI_API_ENDPOINT=https://analytics.infra.mintel.me
# Error Tracking
SENTRY_DSN=your-sentry-dsn
Note: No NEXT_PUBLIC_ prefix is required for these anymore, as they are handled by server-side proxies.
2. Analytics Setup
In your root layout:
import {
AnalyticsContextProvider,
AnalyticsAutoTracker,
UmamiAnalyticsService,
} from "@mintel/observability";
const analytics = new UmamiAnalyticsService({
enabled: true,
websiteId: process.env.UMAMI_WEBSITE_ID, // Server-side
apiEndpoint:
typeof window === "undefined" ? process.env.UMAMI_API_ENDPOINT : "/stats",
});
export default function Layout({ children }) {
return (
<AnalyticsContextProvider service={analytics}>
<AnalyticsAutoTracker />
{children}
</AnalyticsContextProvider>
);
}
3. Route Handlers
Create a proxy for Umami:
app/stats/api/send/route.ts
import { createUmamiProxyHandler } from "@mintel/observability";
export const POST = await createUmamiProxyHandler({
websiteId: process.env.UMAMI_WEBSITE_ID,
apiEndpoint: process.env.UMAMI_API_ENDPOINT,
});
Create a relay for Sentry:
app/errors/api/relay/route.ts
import { createSentryRelayHandler } from "@mintel/observability";
export const POST = await createSentryRelayHandler({
dsn: process.env.SENTRY_DSN,
});
4. Notification Setup (Server-side)
import { GotifyNotificationService } from "@mintel/observability";
const notifications = new GotifyNotificationService({
enabled: true,
url: process.env.GOTIFY_URL,
token: process.env.GOTIFY_TOKEN,
});
await notifications.notify({
title: "Lead Capture",
message: "New contact form submission",
priority: 5,
});
5. Sentry Configuration
Use initSentry in your sentry.server.config.ts and sentry.client.config.ts.
On the client, use the tunnel:
initSentry({
dsn: "https://public@errors.infra.mintel.me/1", // Placeholder
tunnel: "/errors/api/relay",
});
Architecture
This package implements the Smart Proxy pattern:
- The client NEVER knows the real
UMAMI_WEBSITE_ID. - Tracking events are sent to your own domain (
/stats/api/send). - Your server injects the secret ID and forwards to Umami.
- This bypasses ad-blockers and keeps your configuration secure.