60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
/** Canonical origins are server configuration; request headers never choose them. */
|
|
function parseConfiguredUrl(value: string, name: string): URL {
|
|
let url: URL;
|
|
try { url = new URL(value); } catch { throw new Error(`FATAL CONFIGURATION ERROR: ${name} must be a valid URL.`); }
|
|
if (url.username || url.password || url.search || url.hash) {
|
|
throw new Error(`FATAL CONFIGURATION ERROR: ${name} cannot contain credentials, query or fragment.`);
|
|
}
|
|
return url;
|
|
}
|
|
|
|
export function isLocalSingleUserProfile(): boolean {
|
|
if (process.env.DEPLOYMENT_PROFILE !== 'local-single-user') return false;
|
|
const value = process.env.APP_BASE_URL?.trim();
|
|
if (!value) throw new Error('FATAL CONFIGURATION ERROR: APP_BASE_URL is required for local-single-user.');
|
|
const url = parseConfiguredUrl(value, 'APP_BASE_URL');
|
|
if (!['localhost', '127.0.0.1', '[::1]'].includes(url.hostname) || url.protocol !== 'http:' || url.pathname !== '/') {
|
|
throw new Error('FATAL CONFIGURATION ERROR: local-single-user requires an HTTP loopback origin.');
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function getAppBaseUrl(): string {
|
|
const envUrl = process.env.APP_BASE_URL?.trim();
|
|
if (process.env.NODE_ENV === 'production') {
|
|
if (!envUrl) throw new Error('FATAL CONFIGURATION ERROR: APP_BASE_URL environment variable is strictly required in production.');
|
|
const url = parseConfiguredUrl(envUrl, 'APP_BASE_URL');
|
|
if (url.pathname !== '/' || (url.protocol !== 'https:' && !isLocalSingleUserProfile())) {
|
|
throw new Error('FATAL CONFIGURATION ERROR: APP_BASE_URL must be a valid HTTPS URL in production.');
|
|
}
|
|
// Validate explicitly selected local profile even when URL happens to use HTTPS.
|
|
if (process.env.DEPLOYMENT_PROFILE === 'local-single-user') isLocalSingleUserProfile();
|
|
return url.origin;
|
|
}
|
|
return (envUrl || 'http://localhost:3000').replace(/\/+$/, '');
|
|
}
|
|
|
|
export function getVkRedirectUri(): string {
|
|
const envUri = process.env.VK_REDIRECT_URI?.trim();
|
|
if (process.env.NODE_ENV === 'production') {
|
|
if (!envUri) throw new Error('FATAL CONFIGURATION ERROR: VK_REDIRECT_URI environment variable is strictly required in production.');
|
|
const url = parseConfiguredUrl(envUri, 'VK_REDIRECT_URI');
|
|
const base = getAppBaseUrl();
|
|
if (url.origin !== base || url.pathname !== '/api/auth/vk/callback') {
|
|
throw new Error('FATAL CONFIGURATION ERROR: VK_REDIRECT_URI must match the canonical origin and callback path.');
|
|
}
|
|
return url.href;
|
|
}
|
|
return envUri || `${getAppBaseUrl()}/api/auth/vk/callback`;
|
|
}
|
|
|
|
export function getSecureSessionCookieFlag(): boolean {
|
|
if (process.env.NODE_ENV !== 'production') return false;
|
|
if (process.env.DEPLOYMENT_PROFILE === 'local-single-user') return !isLocalSingleUserProfile();
|
|
return true;
|
|
}
|
|
|
|
export function getTrustedHost(): string {
|
|
// Production configuration failures must never fall back to a different trust boundary.
|
|
return new URL(getAppBaseUrl()).host.toLowerCase();
|
|
}
|