diff --git a/docker/middleware.ts b/docker/middleware.ts index 4b189df89..15111884d 100644 --- a/docker/middleware.ts +++ b/docker/middleware.ts @@ -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 }); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f713c1a8..e9e0e7553 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6701,6 +6701,10 @@ packages: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} diff --git a/src/lib/__tests__/match-configured-path.test.ts b/src/lib/__tests__/match-configured-path.test.ts new file mode 100644 index 000000000..f70ba9c1e --- /dev/null +++ b/src/lib/__tests__/match-configured-path.test.ts @@ -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); +}); diff --git a/src/lib/match-configured-path.ts b/src/lib/match-configured-path.ts new file mode 100644 index 000000000..104319f2d --- /dev/null +++ b/src/lib/match-configured-path.ts @@ -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; +}