Fix metadata base URL generation
This commit is contained in:
+13
-6
@@ -1,4 +1,5 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { headers } from 'next/headers';
|
||||
import { Suspense } from 'react';
|
||||
import { Providers } from './Providers';
|
||||
import '@fontsource/inter/300.css';
|
||||
@@ -6,6 +7,7 @@ import '@fontsource/inter/400.css';
|
||||
import '@fontsource/inter/500.css';
|
||||
import '@fontsource/inter/700.css';
|
||||
import '@umami/react-zen/styles.css';
|
||||
import { getBaseUrl } from '@/lib/get-base-url';
|
||||
import '@/styles/global.css';
|
||||
import '@/styles/variables.css';
|
||||
|
||||
@@ -41,9 +43,14 @@ export default function ({ children }) {
|
||||
);
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s | Umami',
|
||||
default: 'Umami',
|
||||
},
|
||||
};
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const headerStore = await headers();
|
||||
|
||||
return {
|
||||
metadataBase: getBaseUrl(headerStore),
|
||||
title: {
|
||||
template: '%s | Umami',
|
||||
default: 'Umami',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { HOMEPAGE_URL } from '../constants';
|
||||
import { getBaseUrl } from '../get-base-url';
|
||||
|
||||
function createHeaders(entries: Record<string, string>) {
|
||||
return {
|
||||
get(name: string) {
|
||||
return entries[name.toLowerCase()] ?? null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('prefers forwarded host and protocol', () => {
|
||||
const url = getBaseUrl(
|
||||
createHeaders({
|
||||
'x-forwarded-host': 'umami.is',
|
||||
'x-forwarded-proto': 'https',
|
||||
host: 'localhost:3000',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(url.toString()).toBe('https://umami.is/');
|
||||
});
|
||||
|
||||
test('falls back to host header', () => {
|
||||
const url = getBaseUrl(
|
||||
createHeaders({
|
||||
host: 'analytics.example.com',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(url.toString()).toBe('https://analytics.example.com/');
|
||||
});
|
||||
|
||||
test('uses http for localhost hosts', () => {
|
||||
const url = getBaseUrl(
|
||||
createHeaders({
|
||||
host: 'localhost:3000',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(url.toString()).toBe('http://localhost:3000/');
|
||||
});
|
||||
|
||||
test('falls back to homepage when host is missing', () => {
|
||||
const url = getBaseUrl(createHeaders({}));
|
||||
|
||||
expect(url.toString()).toBe(`${HOMEPAGE_URL}/`);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { HOMEPAGE_URL } from './constants';
|
||||
|
||||
type HeaderStore = Pick<Headers, 'get'>;
|
||||
|
||||
function getFirstHeaderValue(value?: string | null) {
|
||||
return value?.split(',')[0]?.trim();
|
||||
}
|
||||
|
||||
function getDefaultProtocol(host?: string) {
|
||||
if (!host) {
|
||||
return 'https';
|
||||
}
|
||||
|
||||
if (host.startsWith('localhost') || host.startsWith('127.0.0.1') || host.startsWith('[::1]')) {
|
||||
return 'http';
|
||||
}
|
||||
|
||||
return 'https';
|
||||
}
|
||||
|
||||
export function getBaseUrl(headers?: HeaderStore) {
|
||||
const host =
|
||||
getFirstHeaderValue(headers?.get('x-forwarded-host')) ||
|
||||
getFirstHeaderValue(headers?.get('host'));
|
||||
|
||||
if (!host) {
|
||||
return new URL(HOMEPAGE_URL);
|
||||
}
|
||||
|
||||
const protocol =
|
||||
getFirstHeaderValue(headers?.get('x-forwarded-proto')) ||
|
||||
getFirstHeaderValue(headers?.get('x-forwarded-protocol')) ||
|
||||
getDefaultProtocol(host);
|
||||
|
||||
try {
|
||||
return new URL(`${protocol}://${host}`);
|
||||
} catch {
|
||||
return new URL(HOMEPAGE_URL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user