feat: support subpath deployment with NEXT_PUBLIC_BASE_PATH environment variable
This commit is contained in:
@@ -8,17 +8,27 @@ export function replaceWindowLocation(url: string): void {
|
||||
window.location.replace(url);
|
||||
}
|
||||
|
||||
// Build-time constant injected by next.config.ts. When the app is built with
|
||||
// NEXT_PUBLIC_BASE_PATH=/webmail, Next.js itself prefixes routes and assets;
|
||||
// helpers below use the same value so client code stays consistent.
|
||||
const STATIC_BASE_PATH = (process.env.NEXT_PUBLIC_BASE_PATH ?? '').replace(/\/+$/, '');
|
||||
|
||||
/**
|
||||
* Returns the mount prefix from the current URL.
|
||||
* When the app is served behind a reverse proxy at e.g. /bulwark,
|
||||
* the browser sees /bulwark/en/login while Next.js sees /en/login.
|
||||
* Returns the mount prefix the app is served at.
|
||||
*
|
||||
* If a locale is supplied (e.g. from route params) it is used directly;
|
||||
* otherwise the first path segment that matches a known locale is used.
|
||||
* Resolution order:
|
||||
* 1. The build-time `NEXT_PUBLIC_BASE_PATH` constant (set in next.config.ts).
|
||||
* 2. Runtime detection from `window.location.pathname` for legacy deploys
|
||||
* where the reverse proxy mounts the app at a subpath without rebuilding.
|
||||
*
|
||||
* If a locale is supplied (e.g. from route params) it anchors the runtime
|
||||
* detection; otherwise the first path segment that matches a known locale is
|
||||
* used.
|
||||
*
|
||||
* Returns '' when there is no prefix.
|
||||
*/
|
||||
export function getPathPrefix(locale?: string): string {
|
||||
if (STATIC_BASE_PATH) return STATIC_BASE_PATH;
|
||||
if (typeof window === 'undefined') return '';
|
||||
|
||||
const segments = window.location.pathname.split('/').filter(Boolean);
|
||||
|
||||
+8
-4
@@ -9,6 +9,10 @@ import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
const DEVICE_CLIENT_ID_KEY = 'bulwark.push.deviceClientId.v1';
|
||||
const SUBSCRIPTION_ID_KEY = 'bulwark.push.subscriptionId.v1';
|
||||
|
||||
const BASE_PATH = (process.env.NEXT_PUBLIC_BASE_PATH ?? '').replace(/\/+$/, '');
|
||||
const SW_SCOPE = `${BASE_PATH}/`;
|
||||
const SW_URL = `${BASE_PATH}/sw.js`;
|
||||
|
||||
// Hosted relay so self-hosters don't need their own VAPID + Firebase setup.
|
||||
// Override at build time via NEXT_PUBLIC_PUSH_RELAY_URL or at runtime by
|
||||
// calling enableWebPush({ relayBaseUrl }) from the settings UI.
|
||||
@@ -139,9 +143,9 @@ async function ensureServiceWorker(): Promise<ServiceWorkerRegistration> {
|
||||
// The webmail's PWA already registers /sw.js for installability. If it
|
||||
// hasn't been picked up yet (e.g. first load), kick it ourselves so the
|
||||
// push handler is in place.
|
||||
let registration = await navigator.serviceWorker.getRegistration('/');
|
||||
let registration = await navigator.serviceWorker.getRegistration(SW_SCOPE);
|
||||
if (!registration) {
|
||||
registration = await navigator.serviceWorker.register('/sw.js');
|
||||
registration = await navigator.serviceWorker.register(SW_URL, { scope: SW_SCOPE });
|
||||
}
|
||||
await navigator.serviceWorker.ready;
|
||||
return registration;
|
||||
@@ -336,7 +340,7 @@ export async function disableWebPush(params: DisableWebPushParams): Promise<void
|
||||
}
|
||||
|
||||
if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {
|
||||
const registration = await navigator.serviceWorker.getRegistration('/');
|
||||
const registration = await navigator.serviceWorker.getRegistration(SW_SCOPE);
|
||||
const sub = await registration?.pushManager.getSubscription();
|
||||
if (sub) await sub.unsubscribe().catch(() => undefined);
|
||||
}
|
||||
@@ -345,7 +349,7 @@ export async function disableWebPush(params: DisableWebPushParams): Promise<void
|
||||
export async function isWebPushEnabled(): Promise<boolean> {
|
||||
if (!isWebPushSupported()) return false;
|
||||
if (Notification.permission !== 'granted') return false;
|
||||
const registration = await navigator.serviceWorker.getRegistration('/');
|
||||
const registration = await navigator.serviceWorker.getRegistration(SW_SCOPE);
|
||||
if (!registration) return false;
|
||||
const sub = await registration.pushManager.getSubscription();
|
||||
return sub !== null && localStorage.getItem(SUBSCRIPTION_ID_KEY) !== null;
|
||||
|
||||
Reference in New Issue
Block a user