mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
/**
|
||||
* OAuth Callback Page Content
|
||||
*/
|
||||
function CallbackContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const [status, setStatus] = useState("processing");
|
||||
|
||||
useEffect(() => {
|
||||
const code = searchParams.get("code");
|
||||
const state = searchParams.get("state");
|
||||
const error = searchParams.get("error");
|
||||
const errorDescription = searchParams.get("error_description");
|
||||
|
||||
const callbackData = {
|
||||
code,
|
||||
state,
|
||||
error,
|
||||
errorDescription,
|
||||
fullUrl: window.location.href,
|
||||
};
|
||||
|
||||
let sent = false;
|
||||
|
||||
// Check if this callback is from expected origin/port
|
||||
const expectedOrigins = [
|
||||
window.location.origin, // Same origin (for most providers)
|
||||
"http://localhost:1455", // Codex specific port
|
||||
];
|
||||
|
||||
// Method 1: postMessage to opener (popup mode)
|
||||
if (window.opener) {
|
||||
try {
|
||||
window.opener.postMessage({ type: "oauth_callback", data: callbackData }, "*"); // Allow any origin for local dev
|
||||
sent = true;
|
||||
} catch (e) {
|
||||
console.log("postMessage failed:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: BroadcastChannel (same origin tabs)
|
||||
try {
|
||||
const channel = new BroadcastChannel("oauth_callback");
|
||||
channel.postMessage(callbackData);
|
||||
channel.close();
|
||||
sent = true;
|
||||
} catch (e) {
|
||||
console.log("BroadcastChannel failed:", e);
|
||||
}
|
||||
|
||||
// Method 3: localStorage event (fallback)
|
||||
try {
|
||||
localStorage.setItem("oauth_callback", JSON.stringify({ ...callbackData, timestamp: Date.now() }));
|
||||
sent = true;
|
||||
} catch (e) {
|
||||
console.log("localStorage failed:", e);
|
||||
}
|
||||
|
||||
if (sent && (code || error)) {
|
||||
// Use setTimeout to avoid synchronous setState in effect
|
||||
setTimeout(() => {
|
||||
setStatus("success");
|
||||
// Auto close after 1.5 seconds
|
||||
setTimeout(() => {
|
||||
window.close();
|
||||
// If can't close (not a popup), show success message
|
||||
setTimeout(() => setStatus("done"), 500);
|
||||
}, 1500);
|
||||
}, 0);
|
||||
} else {
|
||||
setTimeout(() => setStatus("manual"), 0);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg">
|
||||
<div className="text-center p-8 max-w-md">
|
||||
{status === "processing" && (
|
||||
<>
|
||||
<div className="size-16 mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<span className="material-symbols-outlined text-3xl text-primary animate-spin">progress_activity</span>
|
||||
</div>
|
||||
<h1 className="text-xl font-semibold mb-2">Processing...</h1>
|
||||
<p className="text-text-muted">Please wait while we complete the authorization.</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(status === "success" || status === "done") && (
|
||||
<>
|
||||
<div className="size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<span className="material-symbols-outlined text-3xl text-green-600">check_circle</span>
|
||||
</div>
|
||||
<h1 className="text-xl font-semibold mb-2">Authorization Successful!</h1>
|
||||
<p className="text-text-muted">
|
||||
{status === "success" ? "This window will close automatically..." : "You can close this tab now."}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{status === "manual" && (
|
||||
<>
|
||||
<div className="size-16 mx-auto mb-4 rounded-full bg-yellow-100 dark:bg-yellow-900/30 flex items-center justify-center">
|
||||
<span className="material-symbols-outlined text-3xl text-yellow-600">info</span>
|
||||
</div>
|
||||
<h1 className="text-xl font-semibold mb-2">Copy This URL</h1>
|
||||
<p className="text-text-muted mb-4">
|
||||
Please copy the URL from the address bar and paste it in the application.
|
||||
</p>
|
||||
<div className="bg-surface border border-border rounded-lg p-3 text-left">
|
||||
<code className="text-xs break-all">{typeof window !== "undefined" ? window.location.href : ""}</code>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth Callback Page
|
||||
* Receives callback from OAuth providers and sends data back via multiple methods
|
||||
*/
|
||||
export default function CallbackPage() {
|
||||
return (
|
||||
<Suspense fallback={
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg">
|
||||
<div className="text-center p-8">
|
||||
<div className="size-16 mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<span className="material-symbols-outlined text-3xl text-primary animate-spin">progress_activity</span>
|
||||
</div>
|
||||
<p className="text-text-muted">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
}>
|
||||
<CallbackContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user