mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-07 11:16:37 +00:00
auto open checkout modal
This commit is contained in:
@@ -10,8 +10,8 @@ import { Badge } from "./ui/badge";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { DataTable } from "./ui/data-table";
|
import { DataTable } from "./ui/data-table";
|
||||||
import { GeneratedLicenseKey } from "@server/routers/generatedLicense/types";
|
import { GeneratedLicenseKey } from "@server/routers/generatedLicense/types";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
@@ -29,12 +29,15 @@ function obfuscateLicenseKey(key: string): string {
|
|||||||
return `${firstPart}••••••••••••••••••••${lastPart}`;
|
return `${firstPart}••••••••••••••••••••${lastPart}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GENERATE_QUERY = "generate";
|
||||||
|
|
||||||
export default function GenerateLicenseKeysTable({
|
export default function GenerateLicenseKeysTable({
|
||||||
licenseKeys,
|
licenseKeys,
|
||||||
orgId
|
orgId
|
||||||
}: GnerateLicenseKeysTableProps) {
|
}: GnerateLicenseKeysTableProps) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
const api = createApiClient({ env });
|
const api = createApiClient({ env });
|
||||||
@@ -42,6 +45,19 @@ export default function GenerateLicenseKeysTable({
|
|||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
const [showGenerateForm, setShowGenerateForm] = useState(false);
|
const [showGenerateForm, setShowGenerateForm] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (searchParams.get(GENERATE_QUERY) !== null) {
|
||||||
|
setShowGenerateForm(true);
|
||||||
|
const next = new URLSearchParams(searchParams);
|
||||||
|
next.delete(GENERATE_QUERY);
|
||||||
|
const qs = next.toString();
|
||||||
|
const url = qs
|
||||||
|
? `${window.location.pathname}?${qs}`
|
||||||
|
: window.location.pathname;
|
||||||
|
window.history.replaceState(null, "", url);
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
const handleLicenseGenerated = () => {
|
const handleLicenseGenerated = () => {
|
||||||
// Refresh the data after license is generated
|
// Refresh the data after license is generated
|
||||||
refreshData();
|
refreshData();
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
type CleanRedirectOptions = {
|
type CleanRedirectOptions = {
|
||||||
fallback?: string;
|
fallback?: string;
|
||||||
maxRedirectDepth?: number;
|
maxRedirectDepth?: number;
|
||||||
|
/** When true, preserve all query params on the path (for internal redirects). Default false. */
|
||||||
|
allowAllQueryParams?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALLOWED_QUERY_PARAMS = new Set([
|
const ALLOWED_QUERY_PARAMS = new Set([
|
||||||
@@ -16,14 +18,18 @@ export function cleanRedirect(
|
|||||||
input: string,
|
input: string,
|
||||||
options: CleanRedirectOptions = {}
|
options: CleanRedirectOptions = {}
|
||||||
): string {
|
): string {
|
||||||
const { fallback = "/", maxRedirectDepth = 2 } = options;
|
const {
|
||||||
|
fallback = "/",
|
||||||
|
maxRedirectDepth = 2,
|
||||||
|
allowAllQueryParams = false
|
||||||
|
} = options;
|
||||||
|
|
||||||
if (!input || typeof input !== "string") {
|
if (!input || typeof input !== "string") {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return sanitizeUrl(input, fallback, maxRedirectDepth);
|
return sanitizeUrl(input, fallback, maxRedirectDepth, allowAllQueryParams);
|
||||||
} catch {
|
} catch {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
@@ -32,7 +38,8 @@ export function cleanRedirect(
|
|||||||
function sanitizeUrl(
|
function sanitizeUrl(
|
||||||
input: string,
|
input: string,
|
||||||
fallback: string,
|
fallback: string,
|
||||||
remainingRedirectDepth: number
|
remainingRedirectDepth: number,
|
||||||
|
allowAllQueryParams: boolean = false
|
||||||
): string {
|
): string {
|
||||||
if (
|
if (
|
||||||
input.startsWith("javascript:") ||
|
input.startsWith("javascript:") ||
|
||||||
@@ -56,7 +63,7 @@ function sanitizeUrl(
|
|||||||
const cleanParams = new URLSearchParams();
|
const cleanParams = new URLSearchParams();
|
||||||
|
|
||||||
for (const [key, value] of url.searchParams.entries()) {
|
for (const [key, value] of url.searchParams.entries()) {
|
||||||
if (!ALLOWED_QUERY_PARAMS.has(key)) {
|
if (!allowAllQueryParams && !ALLOWED_QUERY_PARAMS.has(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +75,8 @@ function sanitizeUrl(
|
|||||||
const cleanedRedirect = sanitizeUrl(
|
const cleanedRedirect = sanitizeUrl(
|
||||||
value,
|
value,
|
||||||
"",
|
"",
|
||||||
remainingRedirectDepth - 1
|
remainingRedirectDepth - 1,
|
||||||
|
allowAllQueryParams
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cleanedRedirect) {
|
if (cleanedRedirect) {
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ export function consumeInternalRedirectPath(): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleaned = cleanRedirect(storedPath, { fallback: "" });
|
const cleaned = cleanRedirect(storedPath, {
|
||||||
|
fallback: "",
|
||||||
|
allowAllQueryParams: true
|
||||||
|
});
|
||||||
if (!cleaned) return null;
|
if (!cleaned) return null;
|
||||||
|
|
||||||
return cleaned.startsWith("/") ? cleaned : `/${cleaned}`;
|
return cleaned.startsWith("/") ? cleaned : `/${cleaned}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user