Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
const INTERNAL_URL_BASE = 'https://notifications.ghabilee.invalid'
|
|
const hasControlCharacter = (value: string) =>
|
|
Array.from(value).some((character) => {
|
|
const code = character.charCodeAt(0)
|
|
|
|
return code <= 31 || code === 127
|
|
})
|
|
|
|
export const NOTIFICATIONS_RETURN_PATH = '/profile/notifications'
|
|
|
|
export const isSafeInternalNavigationPath = (value: string | null | undefined): value is string => {
|
|
if (!value || !value.startsWith('/') || value.startsWith('//') || value.includes('\\') || hasControlCharacter(value)) {
|
|
return false
|
|
}
|
|
|
|
try {
|
|
return new URL(value, INTERNAL_URL_BASE).origin === INTERNAL_URL_BASE
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export const addReturnTo = (path: string, returnTo: string): string | null => {
|
|
if (!isSafeInternalNavigationPath(path) || !isSafeInternalNavigationPath(returnTo)) return null
|
|
|
|
const url = new URL(path, INTERNAL_URL_BASE)
|
|
|
|
url.searchParams.set('returnTo', returnTo)
|
|
|
|
return `${url.pathname}${url.search}${url.hash}`
|
|
}
|
|
|
|
export const buildNotificationActionPath = (actionUrl: string | null | undefined) =>
|
|
actionUrl ? addReturnTo(/^\/bookings\/[^/?#]+(?:[?#]|$)/.test(actionUrl) ? '/my-events' : actionUrl, NOTIFICATIONS_RETURN_PATH) : null
|
|
|
|
export const getSafeReturnTo = (search: string) => {
|
|
const returnTo = new URLSearchParams(search).get('returnTo')
|
|
|
|
return isSafeInternalNavigationPath(returnTo) ? returnTo : null
|
|
}
|