mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-22 12:56:37 +00:00
Merge branch 'dev' into feat/logo-path-in-enterprise
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { build } from "@server/build";
|
||||
import { TierId } from "@server/lib/billing/tiers";
|
||||
import { cache } from "react";
|
||||
import { getCachedSubscription } from "./getCachedSubscription";
|
||||
import { priv } from ".";
|
||||
@@ -21,7 +20,7 @@ export const isOrgSubscribed = cache(async (orgId: string) => {
|
||||
try {
|
||||
const subRes = await getCachedSubscription(orgId);
|
||||
subscribed =
|
||||
subRes.data.data.tier === TierId.STANDARD &&
|
||||
(subRes.data.data.tier == "tier1" || subRes.data.data.tier == "tier2" || subRes.data.data.tier == "tier3") &&
|
||||
subRes.data.data.active;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
@@ -32,7 +32,11 @@ export function pullEnv(): Env {
|
||||
process.env.NEW_RELEASES_NOTIFICATION_ENABLED === "true"
|
||||
? true
|
||||
: false
|
||||
}
|
||||
},
|
||||
identityProviderMode: process.env.IDENTITY_PROVIDER_MODE as
|
||||
| "org"
|
||||
| "global"
|
||||
| undefined
|
||||
},
|
||||
email: {
|
||||
emailEnabled: process.env.EMAIL_ENABLED === "true" ? true : false
|
||||
@@ -64,8 +68,10 @@ export function pullEnv(): Env {
|
||||
process.env.FLAGS_DISABLE_PRODUCT_HELP_BANNERS === "true"
|
||||
? true
|
||||
: false,
|
||||
useOrgOnlyIdp:
|
||||
process.env.USE_ORG_ONLY_IDP === "true" ? true : false
|
||||
disableEnterpriseFeatures:
|
||||
process.env.DISABLE_ENTERPRISE_FEATURES === "true"
|
||||
? true
|
||||
: false
|
||||
},
|
||||
|
||||
branding: {
|
||||
|
||||
@@ -8,6 +8,7 @@ export type Env = {
|
||||
product_updates: boolean;
|
||||
new_releases: boolean;
|
||||
};
|
||||
identityProviderMode?: "global" | "org";
|
||||
};
|
||||
server: {
|
||||
externalPort: string;
|
||||
@@ -34,7 +35,7 @@ export type Env = {
|
||||
hideSupporterKey: boolean;
|
||||
usePangolinDns: boolean;
|
||||
disableProductHelpBanners: boolean;
|
||||
useOrgOnlyIdp: boolean;
|
||||
disableEnterpriseFeatures: boolean;
|
||||
};
|
||||
branding: {
|
||||
appName?: string;
|
||||
|
||||
Reference in New Issue
Block a user