diff --git a/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/page.tsx b/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/page.tsx
index d980279ce..768255a6e 100644
--- a/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/page.tsx
+++ b/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/page.tsx
@@ -1,7 +1,12 @@
import type { Metadata } from 'next';
+import { notFound } from 'next/navigation';
import { HeatmapsPage } from './HeatmapsPage';
export default async function ({ params }: { params: Promise<{ websiteId: string }> }) {
+ if (!process.env.UMAMI_SELF_RECORD) {
+ notFound();
+ }
+
const { websiteId } = await params;
return ;
diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts
index 101a12243..56a60bed8 100644
--- a/src/app/api/config/route.ts
+++ b/src/app/api/config/route.ts
@@ -14,6 +14,7 @@ export async function GET(request: Request) {
linksUrl: process.env.LINKS_URL,
pixelsUrl: process.env.PIXELS_URL,
privateMode: !!process.env.PRIVATE_MODE,
+ selfRecordEnabled: !!process.env.UMAMI_SELF_RECORD,
telemetryDisabled: !!process.env.DISABLE_TELEMETRY,
trackerScriptName: process.env.TRACKER_SCRIPT_NAME,
updatesDisabled: !!process.env.DISABLE_UPDATES,
diff --git a/src/app/api/reports/heatmap/route.ts b/src/app/api/reports/heatmap/route.ts
index 79af35c3e..5c3b1844b 100644
--- a/src/app/api/reports/heatmap/route.ts
+++ b/src/app/api/reports/heatmap/route.ts
@@ -1,10 +1,14 @@
import { parseRequest, setWebsiteDate } from '@/lib/request';
-import { json, unauthorized } from '@/lib/response';
+import { forbidden, json, unauthorized } from '@/lib/response';
import { reportResultSchema } from '@/lib/schema';
import { canViewWebsite } from '@/permissions';
import { getHeatmap, type HeatmapParameters } from '@/queries/sql';
export async function POST(request: Request) {
+ if (!process.env.UMAMI_SELF_RECORD) {
+ return forbidden({ message: 'Heatmap recording is disabled.' });
+ }
+
const { auth, body, error } = await parseRequest(request, reportResultSchema);
if (error) {
diff --git a/src/components/hooks/useConfig.ts b/src/components/hooks/useConfig.ts
index c1cdcaf67..baf0b4828 100644
--- a/src/components/hooks/useConfig.ts
+++ b/src/components/hooks/useConfig.ts
@@ -8,6 +8,7 @@ export type Config = {
linksUrl?: string;
pixelsUrl?: string;
privateMode: boolean;
+ selfRecordEnabled: boolean;
telemetryDisabled: boolean;
trackerScriptName?: string;
updatesDisabled: boolean;
diff --git a/src/components/hooks/useWebsiteNavItems.tsx b/src/components/hooks/useWebsiteNavItems.tsx
index 09731f101..37b661d03 100644
--- a/src/components/hooks/useWebsiteNavItems.tsx
+++ b/src/components/hooks/useWebsiteNavItems.tsx
@@ -11,12 +11,14 @@ import {
Video,
} from '@/components/icons';
import { Funnel, Gauge, Lightning, Magnet, Money, Network, Path, Target } from '@/components/svg';
+import { useConfig } from './useConfig';
import { useMessages } from './useMessages';
import { useNavigation } from './useNavigation';
export function useWebsiteNavItems(websiteId: string) {
const { t, labels } = useMessages();
const { pathname, renderUrl } = useNavigation();
+ const config = useConfig();
const renderPath = (path: string) =>
renderUrl(`/websites/${websiteId}${path}`, {
@@ -108,12 +110,16 @@ export function useWebsiteNavItems(websiteId: string) {
icon: ,
path: renderPath('/replays'),
},
- {
- id: 'heatmaps',
- label: t(labels.heatmaps),
- icon: ,
- path: renderPath('/heatmaps'),
- },
+ ...(config?.selfRecordEnabled
+ ? [
+ {
+ id: 'heatmaps',
+ label: t(labels.heatmaps),
+ icon: ,
+ path: renderPath('/heatmaps'),
+ },
+ ]
+ : []),
],
},
{