mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-22 12:56:37 +00:00
Merge branch 'dev' into refactor/paginated-tables
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
type CleanRedirectOptions = {
|
||||
fallback?: string;
|
||||
maxRedirectDepth?: number;
|
||||
/** When true, preserve all query params on the path (for internal redirects). Default false. */
|
||||
allowAllQueryParams?: boolean;
|
||||
};
|
||||
|
||||
const ALLOWED_QUERY_PARAMS = new Set([
|
||||
@@ -16,14 +18,18 @@ export function cleanRedirect(
|
||||
input: string,
|
||||
options: CleanRedirectOptions = {}
|
||||
): string {
|
||||
const { fallback = "/", maxRedirectDepth = 2 } = options;
|
||||
const {
|
||||
fallback = "/",
|
||||
maxRedirectDepth = 2,
|
||||
allowAllQueryParams = false
|
||||
} = options;
|
||||
|
||||
if (!input || typeof input !== "string") {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
try {
|
||||
return sanitizeUrl(input, fallback, maxRedirectDepth);
|
||||
return sanitizeUrl(input, fallback, maxRedirectDepth, allowAllQueryParams);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
@@ -32,7 +38,8 @@ export function cleanRedirect(
|
||||
function sanitizeUrl(
|
||||
input: string,
|
||||
fallback: string,
|
||||
remainingRedirectDepth: number
|
||||
remainingRedirectDepth: number,
|
||||
allowAllQueryParams: boolean = false
|
||||
): string {
|
||||
if (
|
||||
input.startsWith("javascript:") ||
|
||||
@@ -56,7 +63,7 @@ function sanitizeUrl(
|
||||
const cleanParams = new URLSearchParams();
|
||||
|
||||
for (const [key, value] of url.searchParams.entries()) {
|
||||
if (!ALLOWED_QUERY_PARAMS.has(key)) {
|
||||
if (!allowAllQueryParams && !ALLOWED_QUERY_PARAMS.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -68,7 +75,8 @@ function sanitizeUrl(
|
||||
const cleanedRedirect = sanitizeUrl(
|
||||
value,
|
||||
"",
|
||||
remainingRedirectDepth - 1
|
||||
remainingRedirectDepth - 1,
|
||||
allowAllQueryParams
|
||||
);
|
||||
|
||||
if (cleanedRedirect) {
|
||||
|
||||
51
src/lib/internalRedirect.ts
Normal file
51
src/lib/internalRedirect.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { cleanRedirect } from "@app/lib/cleanRedirect";
|
||||
|
||||
export const INTERNAL_REDIRECT_KEY = "internal_redirect";
|
||||
|
||||
/**
|
||||
* Consumes the internal_redirect value from localStorage if present and valid
|
||||
* (within TTL). Removes it from storage. Returns the path segment (with leading
|
||||
* slash) to append to an orgId, or null if none/expired/invalid.
|
||||
*/
|
||||
export function consumeInternalRedirectPath(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const raw = window.localStorage.getItem(INTERNAL_REDIRECT_KEY);
|
||||
if (raw == null || raw === "") return null;
|
||||
|
||||
window.localStorage.removeItem(INTERNAL_REDIRECT_KEY);
|
||||
|
||||
const { path: storedPath, expiresAt } = JSON.parse(raw) as {
|
||||
path?: string;
|
||||
expiresAt?: number;
|
||||
};
|
||||
if (
|
||||
typeof storedPath !== "string" ||
|
||||
storedPath === "" ||
|
||||
typeof expiresAt !== "number" ||
|
||||
Date.now() > expiresAt
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cleaned = cleanRedirect(storedPath, {
|
||||
fallback: "",
|
||||
allowAllQueryParams: true
|
||||
});
|
||||
if (!cleaned) return null;
|
||||
|
||||
return cleaned.startsWith("/") ? cleaned : `/${cleaned}`;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full redirect target for an org: either `/${orgId}` or
|
||||
* `/${orgId}${path}` if a valid internal_redirect was stored. Consumes the
|
||||
* stored value.
|
||||
*/
|
||||
export function getInternalRedirectTarget(orgId: string): string {
|
||||
const path = consumeInternalRedirectPath();
|
||||
return path ? `/${orgId}${path}` : `/${orgId}`;
|
||||
}
|
||||
Reference in New Issue
Block a user