Fix tracker script name route matching

This commit is contained in:
cryst
2026-03-09 19:56:41 +00:00
parent 0a838649b7
commit 25081d690e
3 changed files with 61 additions and 4 deletions
+6 -4
View File
@@ -1,4 +1,5 @@
import { type NextRequest, NextResponse } from 'next/server';
import { matchesConfiguredPath } from '@/lib/match-configured-path';
export const config = {
matcher: '/:path*',
@@ -7,6 +8,7 @@ export const config = {
const TRACKER_PATH = '/script.js';
const COLLECT_PATH = '/api/send';
const LOGIN_PATH = '/login';
const BASE_PATH = process.env.BASE_PATH || '';
const apiHeaders = {
'Access-Control-Allow-Origin': '*',
@@ -27,7 +29,7 @@ function customCollectEndpoint(request: NextRequest) {
if (collectEndpoint) {
const url = request.nextUrl.clone();
if (url.pathname.endsWith(collectEndpoint)) {
if (matchesConfiguredPath(url.pathname, collectEndpoint, BASE_PATH)) {
url.pathname = COLLECT_PATH;
return NextResponse.rewrite(url, { headers: apiHeaders });
}
@@ -41,7 +43,7 @@ function customScriptName(request: NextRequest) {
const url = request.nextUrl.clone();
const names = scriptName.split(',').map(name => name.trim().replace(/^\/+/, ''));
if (names.find(name => url.pathname.endsWith(name))) {
if (names.find(name => matchesConfiguredPath(url.pathname, name, BASE_PATH))) {
url.pathname = TRACKER_PATH;
return NextResponse.rewrite(url, { headers: trackerHeaders });
}
@@ -51,7 +53,7 @@ function customScriptName(request: NextRequest) {
function customScriptUrl(request: NextRequest) {
const scriptUrl = process.env.TRACKER_SCRIPT_URL;
if (scriptUrl && request.nextUrl.pathname.endsWith(TRACKER_PATH)) {
if (scriptUrl && matchesConfiguredPath(request.nextUrl.pathname, TRACKER_PATH, BASE_PATH)) {
return NextResponse.rewrite(scriptUrl, { headers: trackerHeaders });
}
}
@@ -59,7 +61,7 @@ function customScriptUrl(request: NextRequest) {
function disableLogin(request: NextRequest) {
const loginDisabled = process.env.DISABLE_LOGIN;
if (loginDisabled && request.nextUrl.pathname.endsWith(LOGIN_PATH)) {
if (loginDisabled && matchesConfiguredPath(request.nextUrl.pathname, LOGIN_PATH, BASE_PATH)) {
return new NextResponse('Access denied', { status: 403 });
}
}
@@ -0,0 +1,17 @@
import { matchesConfiguredPath } from '../match-configured-path';
test('matches the exact configured path', () => {
expect(matchesConfiguredPath('/d.js', 'd.js')).toBe(true);
});
test('does not match unrelated asset paths that only share the suffix', () => {
expect(matchesConfiguredPath('/_next/static/chunks/app/dashboard.js', 'd.js')).toBe(false);
});
test('matches paths under the configured base path', () => {
expect(matchesConfiguredPath('/umami/d.js', 'd.js', '/umami')).toBe(true);
});
test('normalizes leading slashes in configured paths', () => {
expect(matchesConfiguredPath('/script.js', '/script.js')).toBe(true);
});
+38
View File
@@ -0,0 +1,38 @@
function normalizePathname(pathname?: string) {
if (!pathname) {
return '';
}
return `/${pathname.replace(/^\/+/, '')}`;
}
function normalizeBasePath(basePath?: string) {
if (!basePath) {
return '';
}
return `/${basePath.replace(/^\/+|\/+$/g, '')}`;
}
export function matchesConfiguredPath(
pathname: string,
configuredPath?: string,
basePath?: string,
) {
const normalizedPathname = normalizePathname(pathname);
const normalizedConfiguredPath = normalizePathname(configuredPath);
if (!normalizedConfiguredPath) {
return false;
}
if (normalizedPathname === normalizedConfiguredPath) {
return true;
}
const normalizedBasePath = normalizeBasePath(basePath);
return normalizedBasePath
? normalizedPathname === `${normalizedBasePath}${normalizedConfiguredPath}`
: false;
}