-
Notifications
You must be signed in to change notification settings - Fork 15
/
middleware.ts
140 lines (117 loc) · 4.44 KB
/
middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { getTenantConciseInfo } from './src/utils/multiTenancy/helpers';
import { match as matchLocale } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';
import { i18nConfig } from './i18n-config';
function getLocale(
request: NextRequest,
supportedLocales: string[]
): string | undefined {
try {
// Negotiator expects plain object so we need to transform headers
const negotiatorHeaders: Record<string, string> = {};
request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
// Use negotiator and intl-localematcher to get best locale
const languages = new Negotiator({ headers: negotiatorHeaders }).languages(
supportedLocales
);
const previouslySelectedLanguage =
request.cookies.get('NEXT_LOCALE')?.value;
if (
previouslySelectedLanguage !== undefined &&
supportedLocales.includes(previouslySelectedLanguage) &&
languages[0] !== previouslySelectedLanguage
) {
languages.unshift(previouslySelectedLanguage);
}
const locale = matchLocale(
languages,
supportedLocales,
i18nConfig.defaultLocale
);
return locale;
} catch (error) {
console.error('Error occurred while determining the locale:', error);
return i18nConfig.defaultLocale;
}
}
/** Identifies locale in relative url and removes it */
function removeLocaleFromUrl(pathname: string): string {
// For simplicity, we assume that locale will always be in the format of xx or xx-XX
const localeRegex = /^[a-z]{2}(-[a-z]{2})?$/i;
const splitPathname = pathname.split('/');
if (splitPathname.length < 2) return pathname;
const firstSegment = splitPathname[1];
const splitFirstSegment = firstSegment.split('-');
if (localeRegex.test(splitFirstSegment[0])) {
return splitPathname.slice(2).join('/'); //returns the new pathname without the locale
}
return pathname;
}
export const config = {
matcher: [
// This regular expression matches any string except those containing "api", "static", files with extensions, or "_next".
'/((?!api|static|.*\\..*|_next).*)',
'/',
'/sites/:slug*',
],
};
export default async function middleware(req: NextRequest) {
console.log('Starting middleware...');
const start = Date.now();
const url = req.nextUrl;
const pathname = url.pathname;
console.log('pathname', pathname);
const host = req.headers.get('host') as string;
const { slug, supportedLanguages } = await getTenantConciseInfo(host);
console.log('Fetched tenant concise info');
// Filters i18nConfig.locales to only include tenant supported languages
const commonSupportedLocales =
supportedLanguages?.filter((lang) => i18nConfig.locales.includes(lang)) ??
i18nConfig.locales;
const isLocaleMissing = commonSupportedLocales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
);
console.log('isLocaleMissing', isLocaleMissing);
if (isLocaleMissing) {
const locale = getLocale(req, commonSupportedLocales);
const cleanPathname = removeLocaleFromUrl(pathname);
const searchParams = req.nextUrl.search;
const newUrl = new URL(
`/${locale}${
cleanPathname.startsWith('/') ? '' : '/'
}${cleanPathname}${searchParams}`,
req.url
);
console.log('Populated locale, redirecting to:', newUrl.pathname);
return NextResponse.redirect(newUrl);
}
// Prevent security issues – users should not be able to canonically access
// the pages/sites folder and its respective contents.
if (url.pathname.startsWith(`/sites`)) {
url.pathname = `/404`;
} else {
// rewrite to the current subdomain under the pages/sites folder
url.pathname = `/sites/${slug}${url.pathname}`;
}
const res = NextResponse.rewrite(url);
console.log('Rewritten URL:', url.pathname);
// store NEXT_LOCALE cookie if available
const localeFromPath = pathname.split('/')[1];
const localeCookieValue = req.cookies.get('NEXT_LOCALE')?.value;
if (
i18nConfig.locales.includes(localeFromPath) &&
localeFromPath !== localeCookieValue
) {
res.cookies.set('NEXT_LOCALE', localeFromPath, {
maxAge: 31536000, // 1 year
path: '/',
sameSite: 'lax',
secure: process.env.NODE_ENV !== 'development',
});
console.log('Set NEXT_LOCALE cookie:', localeFromPath);
}
console.log('Running time:', Date.now() - start, 'ms');
return res;
}