mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat: ( dmoney ) create redirect page to dmoney
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# API Configuration
|
||||
NEXT_PUBLIC_API_URL=https://your-api-domain.com
|
||||
|
||||
# Allowlisted destination hosts for the /go D-Money redirect bounce page (comma-separated).
|
||||
# The /go?url=... page only forwards to https hosts that match one of these (host or subdomain).
|
||||
NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS=d-money.dj
|
||||
|
||||
# GitHub Packages Token
|
||||
GITHUB_PACKAGE_TOKEN=$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
|
||||
100
apps/edr-passenger-web/portal/src/app/go/page.tsx
Normal file
100
apps/edr-passenger-web/portal/src/app/go/page.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect, useMemo } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Loader2, ShieldAlert, ExternalLink } from "lucide-react";
|
||||
|
||||
|
||||
|
||||
const ALLOWED_HOSTS = (
|
||||
process.env.NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS ?? "d-money.dj"
|
||||
)
|
||||
.split(",")
|
||||
.map((h) => h.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
|
||||
/** True only for https URLs whose host is (or is a subdomain of) an allowlisted host. */
|
||||
function isTrustedDMoneyUrl(raw: string | null): raw is string {
|
||||
if (!raw) return false;
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(raw);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
if (parsed.protocol !== "https:") return false;
|
||||
const host = parsed.hostname.toLowerCase();
|
||||
return ALLOWED_HOSTS.some(
|
||||
(allowed) => host === allowed || host.endsWith(`.${allowed}`),
|
||||
);
|
||||
}
|
||||
|
||||
const REDIRECT_DELAY_MS = 1000;
|
||||
|
||||
function RedirectView() {
|
||||
const searchParams = useSearchParams();
|
||||
const raw = searchParams.get("url");
|
||||
const target = useMemo(() => (isTrustedDMoneyUrl(raw) ? raw : null), [raw]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!target) return;
|
||||
const timer = setTimeout(() => {
|
||||
|
||||
window.location.replace(target);
|
||||
}, REDIRECT_DELAY_MS);
|
||||
return () => clearTimeout(timer);
|
||||
}, [target]);
|
||||
|
||||
if (!target) {
|
||||
return (
|
||||
<div className="w-full max-w-sm text-center">
|
||||
<div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-red-100">
|
||||
<ShieldAlert className="h-8 w-8 text-red-600" />
|
||||
</div>
|
||||
<h1 className="mb-2 text-xl font-bold text-gray-900">
|
||||
Can't continue
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
This link is missing a valid D-Money checkout address or points to an
|
||||
untrusted destination. Please start the payment again from the app.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-sm text-center">
|
||||
<div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
</div>
|
||||
<h1 className="mb-2 text-xl font-bold text-gray-900">
|
||||
Redirecting to D-Money
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
Taking you to the secure D-Money checkout to complete your payment…
|
||||
</p>
|
||||
|
||||
<a
|
||||
href={target}
|
||||
className="mt-6 inline-flex items-center justify-center gap-2 text-sm font-medium text-primary hover:underline"
|
||||
>
|
||||
Continue to D-Money
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GoPage() {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-white px-4">
|
||||
<Suspense
|
||||
fallback={
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" aria-label="Loading" />
|
||||
}
|
||||
>
|
||||
<RedirectView />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user