Files
pleno-vue/src/middleware/originMigration.js
T
Jeppe Bundgaard 0f37b0632a Add POS order booking modal and utilities with tests:
- Introduced `PosDesktopOrderBookingSelectorModal.vue` to streamline booking selection for desktop POS.
- Added `orderBookingDisplay.js` utility for handling booking display and reference value normalization.
- Created unit test `select-vehicle-form-pos.spec.js` to validate booking selection flow and registration matching.
- Implemented `run-playwright-ci-parallel.mjs` script to optimize Playwright CI with parallel testing.
- Updated styles and responsiveness for enhanced booking modal UX.
2026-04-13 21:12:34 +02:00

127 lines
3.2 KiB
JavaScript

export const OUTDATED_INSTALLATION_ROUTE_NAME = 'outdated-installation';
export const OUTDATED_GATEWAY_ROUTE_NAME = 'outdated-gateway';
const EXEMPT_ROUTE_NAMES = new Set([
OUTDATED_INSTALLATION_ROUTE_NAME,
OUTDATED_GATEWAY_ROUTE_NAME,
]);
const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);
const isLoopbackOrigin = (currentOrigin) => {
if (!currentOrigin) return false;
try {
const url = new URL(currentOrigin);
return LOOPBACK_HOSTNAMES.has(url.hostname);
} catch (_error) {
return false;
}
};
const normalizeAllowedOrigins = (allowedOrigins = []) => {
return new Set(
allowedOrigins
.map((origin) => {
try {
return new URL(origin).origin;
} catch (_error) {
return origin;
}
})
.filter(Boolean),
);
};
export const isOriginAllowed = (currentOrigin, allowedOrigins = []) => {
if (!currentOrigin) return true;
if (isLoopbackOrigin(currentOrigin)) return true;
const allowedOriginSet = normalizeAllowedOrigins(allowedOrigins);
return allowedOriginSet.has(currentOrigin);
};
export const isStandaloneContext = (win) => {
const resolvedWindow = win || (typeof window !== 'undefined' ? window : undefined);
if (!resolvedWindow) return false;
const iosStandalone = resolvedWindow.navigator?.standalone === true;
const mediaStandalone = typeof resolvedWindow.matchMedia === 'function'
&& resolvedWindow.matchMedia('(display-mode: standalone)').matches;
return iosStandalone || mediaStandalone;
};
export const shouldBypassOriginGate = (route) => {
return EXEMPT_ROUTE_NAMES.has(route?.name);
};
export const resolveOriginMigrationRoute = ({
to,
currentOrigin,
allowedOrigins,
standalone,
}) => {
if (shouldBypassOriginGate(to)) {
return null;
}
if (isOriginAllowed(currentOrigin, allowedOrigins)) {
return null;
}
if (standalone) {
return { name: OUTDATED_INSTALLATION_ROUTE_NAME };
}
return {
name: OUTDATED_GATEWAY_ROUTE_NAME,
query: {
redirect: to?.fullPath || '/',
},
};
};
export const sanitizeRedirectPath = (redirectPath) => {
if (typeof redirectPath !== 'string') return '/';
if (!redirectPath.startsWith('/')) return '/';
return redirectPath;
};
export const buildMigrationUrl = (migrationOrigin, redirectPath) => {
const safePath = sanitizeRedirectPath(redirectPath);
try {
return new URL(safePath, migrationOrigin).toString();
} catch (_error) {
return migrationOrigin;
}
};
export const startRedirectCountdown = ({
seconds = 5,
onTick,
onRedirect,
setIntervalFn = setInterval,
clearIntervalFn = clearInterval,
setTimeoutFn = setTimeout,
clearTimeoutFn = clearTimeout,
}) => {
let remainingSeconds = seconds;
const intervalId = setIntervalFn(() => {
remainingSeconds = Math.max(remainingSeconds - 1, 0);
if (typeof onTick === 'function') {
onTick(remainingSeconds);
}
if (remainingSeconds === 0) {
clearIntervalFn(intervalId);
}
}, 1000);
const timeoutId = setTimeoutFn(() => {
if (typeof onRedirect === 'function') {
onRedirect();
}
}, seconds * 1000);
return () => {
clearIntervalFn(intervalId);
clearTimeoutFn(timeoutId);
};
};