Skip to content

Commit 6af7311

Browse files
Implement runtime BASE_PATH support with middleware
Co-authored-by: nickshanks347 <11299982+nickshanks347@users.noreply.github.com>
1 parent 1ce979e commit 6af7311

4 files changed

Lines changed: 124 additions & 1 deletion

File tree

.docker/compose-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ services:
1212
- SONARR_API_KEY=YOUR_SONARR_API_KEY
1313
- RADARR_URL=http://radarr:7878
1414
- RADARR_API_KEY=YOUR_RADARR_API_KEY
15+
- BASE_PATH=/monitarr # Test subfolder hosting
1516
restart: unless-stopped

lib/basePath.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Get the base path from environment variable
3+
* This is used to support hosting the app in a subfolder
4+
*/
5+
export function getBasePath(): string {
6+
// On the client side, we can get the base path from the environment
7+
if (typeof window !== 'undefined') {
8+
return process.env.NEXT_PUBLIC_BASE_PATH || '';
9+
}
10+
11+
// On the server side, use the environment variable
12+
return process.env.BASE_PATH || '';
13+
}
14+
15+
/**
16+
* Create a URL that respects the base path configuration
17+
*/
18+
export function createUrl(path: string): string {
19+
const basePath = getBasePath();
20+
21+
// Remove leading slash from path if it exists
22+
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
23+
24+
// If no base path, just return the path with leading slash
25+
if (!basePath) {
26+
return `/${cleanPath}`;
27+
}
28+
29+
// Combine base path with the route path
30+
return `${basePath}/${cleanPath}`;
31+
}
32+
33+
/**
34+
* Get the current base path for client-side usage
35+
* This extracts the base path from the current URL
36+
*/
37+
export function getCurrentBasePath(): string {
38+
if (typeof window === 'undefined') {
39+
return getBasePath();
40+
}
41+
42+
const pathname = window.location.pathname;
43+
44+
// Known application routes - adjust these based on your app structure
45+
const appRoutes = ['/', '/api'];
46+
47+
// Check if current path contains any known route
48+
for (const route of appRoutes) {
49+
const routeIndex = pathname.indexOf(route);
50+
if (routeIndex > 0) {
51+
return pathname.substring(0, routeIndex);
52+
}
53+
}
54+
55+
// If we're at root and there might be a base path
56+
if (pathname !== '/') {
57+
// Check if this could be a base path by looking for common patterns
58+
const segments = pathname.split('/').filter(Boolean);
59+
if (segments.length === 1) {
60+
return `/${segments[0]}`;
61+
}
62+
}
63+
64+
return '';
65+
}

middleware.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
3+
export function middleware(request: NextRequest) {
4+
const basePath = process.env.BASE_PATH || '';
5+
6+
// If no base path is configured, proceed normally
7+
if (!basePath) {
8+
return NextResponse.next();
9+
}
10+
11+
const url = request.nextUrl.clone();
12+
13+
// Skip middleware for static files and internal Next.js routes
14+
if (
15+
url.pathname.startsWith('/_next') ||
16+
url.pathname.startsWith('/api/_next') ||
17+
url.pathname.includes('.')
18+
) {
19+
return NextResponse.next();
20+
}
21+
22+
// Handle requests that already include the base path
23+
if (url.pathname.startsWith(basePath)) {
24+
// Remove the base path from the pathname for internal routing
25+
const newPathname = url.pathname.slice(basePath.length) || '/';
26+
url.pathname = newPathname;
27+
return NextResponse.rewrite(url);
28+
}
29+
30+
// Handle API routes - they should work under base path
31+
if (url.pathname.startsWith('/api')) {
32+
// Rewrite API calls to work under base path
33+
return NextResponse.next();
34+
}
35+
36+
// For all other routes (including root), redirect to include base path
37+
if (!url.pathname.startsWith(basePath)) {
38+
url.pathname = basePath + url.pathname;
39+
return NextResponse.redirect(url);
40+
}
41+
42+
return NextResponse.next();
43+
}
44+
45+
export const config = {
46+
matcher: [
47+
/*
48+
* Match all request paths except for the ones starting with:
49+
* - _next/static (static files)
50+
* - _next/image (image optimization files)
51+
* - favicon.ico (favicon file)
52+
*/
53+
'/((?!_next/static|_next/image|favicon.ico).*)',
54+
],
55+
};

next.config.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
33
output: 'standalone',
4-
basePath: process.env.BASE_PATH || '',
4+
env: {
5+
NEXT_PUBLIC_BASE_PATH: process.env.BASE_PATH || '',
6+
},
57
};
68

79
export default nextConfig;

0 commit comments

Comments
 (0)