hugex-gh / app /routes /_index.tsx
drbh
fix: improve token rention
c202a37
import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData, Link, useSearchParams } from "@remix-run/react";
import { getUserSession } from "~/lib/session.server";
import { accountLinkingService } from "~/lib/account-linking.server";
export const meta: MetaFunction = () => {
return [
{ title: "GitHub + HuggingFace Account Linking" },
{ name: "description", content: "Link your GitHub and HuggingFace accounts" },
];
};
export async function loader({ request }: LoaderFunctionArgs) {
const userSession = await getUserSession(request);
const url = new URL(request.url);
const error = url.searchParams.get("error");
const details = url.searchParams.get("details");
const message = url.searchParams.get("message");
const cookies = request.headers.get("Cookie") || "";
console.log("Cookie keys available:", cookies.split(";").map(c => c.trim().split("=")[0]));
// Get linking stats if available
let linkingStats = null;
if (userSession) {
linkingStats = accountLinkingService.getStats();
}
return json({
userSession,
error,
details,
message,
linkingStats
});
}
export default function Index() {
const { userSession, error, details, message, linkingStats } = useLoaderData<typeof loader>();
const [searchParams] = useSearchParams();
// Determine authentication status
const hasGitHub = !!userSession?.github;
const hasHuggingFace = !!userSession?.huggingface;
const isLinked = userSession?.isLinked || false;
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<div className="flex h-screen items-center justify-center">
<div className="flex flex-col items-center gap-8 max-w-2xl mx-auto px-4">
<header className="flex flex-col items-center gap-6">
<div className="flex space-x-4">
<div className="bg-white rounded-full p-4 shadow-lg">
<svg
className="w-16 h-16 text-blue-600"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
</div>
<div className="bg-white rounded-full p-4 shadow-lg">
<svg
className="w-16 h-16 text-yellow-500"
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm0 2.25c2.387 0 4.5 1.773 4.5 4.5S14.387 13.5 12 13.5 7.5 11.727 7.5 9s2.113-4.5 4.5-4.5zm0 16.5a9.26 9.26 0 01-6.75-2.798c.042-2.233 4.5-3.452 6.75-3.452s6.708 1.219 6.75 3.452A9.26 9.26 0 0112 21z" />
</svg>
</div>
</div>
<h1 className="text-4xl font-bold text-gray-900">
Account Linking
</h1>
<p className="text-lg text-gray-600 text-center">
Link your GitHub and HuggingFace accounts in one place
</p>
</header>
{/* Error Messages */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 w-full">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
Authentication Error
</h3>
<div className="mt-2 text-sm text-red-700">
{error === "oauth_failed" && (
<div>
<p>OAuth authentication failed.</p>
{details && <p className="mt-1 font-mono text-xs">Details: {details}</p>}
</div>
)}
{error === "no_code" && "No authorization code received."}
{error === "callback_failed" && (
<div>
<p>Failed to complete authentication.</p>
{message && <p className="mt-1 font-mono text-xs">Error: {message}</p>}
</div>
)}
{error === "hf_oauth_not_configured" && (
<div>
<p>HuggingFace OAuth is not properly configured.</p>
<p className="mt-1">Please set up the required environment variables.</p>
</div>
)}
{error === "hf_oauth_failed" && (
<div>
<p>HuggingFace authentication failed.</p>
{details && <p className="mt-1 font-mono text-xs">Details: {details}</p>}
</div>
)}
</div>
</div>
</div>
</div>
)}
{/* Main Account Linking Interface */}
<div className="bg-white rounded-lg shadow-lg p-8 w-full">
<h2 className="text-2xl font-bold text-gray-900 mb-6 text-center">
Account Status
</h2>
<div className="grid grid-cols-2 gap-8 mb-8">
{/* GitHub Authentication Status */}
<div className="border rounded-lg p-6 flex flex-col items-center">
<div className={`p-3 rounded-full ${hasGitHub ? 'bg-green-100' : 'bg-gray-100'} mb-4`}>
<svg
className={`w-8 h-8 ${hasGitHub ? 'text-green-600' : 'text-gray-400'}`}
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">GitHub</h3>
{hasGitHub ? (
<div className="text-center">
<div className="flex items-center justify-center mb-2">
{userSession?.github?.avatar_url && (
<img
src={userSession.github.avatar_url}
alt={userSession.github.login}
className="w-12 h-12 rounded-full mr-3"
/>
)}
<div>
<p className="font-medium text-gray-900">{userSession?.github?.name || userSession?.github?.login}</p>
<p className="text-sm text-gray-600">@{userSession?.github?.login}</p>
</div>
</div>
<div className="mt-4">
<Link
to="/auth/logout?service=github"
className="text-sm text-red-600 hover:text-red-800"
>
Disconnect
</Link>
</div>
</div>
) : (
<div className="text-center">
<p className="text-gray-500 mb-4">Not connected</p>
<Link
to="/auth/github"
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-gray-900 hover:bg-gray-800 focus:outline-none"
>
<svg className="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
Connect GitHub
</Link>
</div>
)}
</div>
{/* HuggingFace Authentication Status */}
<div className="border rounded-lg p-6 flex flex-col items-center">
<div className={`p-3 rounded-full ${hasHuggingFace ? 'bg-green-100' : 'bg-gray-100'} mb-4`}>
<svg
className={`w-8 h-8 ${hasHuggingFace ? 'text-green-600' : 'text-gray-400'}`}
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm0 2.25c2.387 0 4.5 1.773 4.5 4.5S14.387 13.5 12 13.5 7.5 11.727 7.5 9s2.113-4.5 4.5-4.5zm0 16.5a9.26 9.26 0 01-6.75-2.798c.042-2.233 4.5-3.452 6.75-3.452s6.708 1.219 6.75 3.452A9.26 9.26 0 0112 21z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">HuggingFace</h3>
{hasHuggingFace ? (
<div className="text-center">
<div className="flex items-center justify-center mb-2">
{userSession?.huggingface?.avatarUrl && (
<img
src={userSession.huggingface.avatarUrl}
alt={userSession.huggingface.username}
className="w-12 h-12 rounded-full mr-3"
/>
)}
<div>
<p className="font-medium text-gray-900">{userSession?.huggingface?.fullName || userSession?.huggingface?.username}</p>
<p className="text-sm text-gray-600">@{userSession?.huggingface?.username}</p>
</div>
</div>
<div className="mt-4">
<Link
to="/auth/logout?service=huggingface"
className="text-sm text-red-600 hover:text-red-800"
>
Disconnect
</Link>
</div>
</div>
) : (
<div className="text-center">
<p className="text-gray-500 mb-4">Not connected</p>
<Link
to="/auth/huggingface"
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-yellow-600 hover:bg-yellow-700 focus:outline-none"
>
<svg
className="w-4 h-4 mr-2"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm0 2.25c2.387 0 4.5 1.773 4.5 4.5S14.387 13.5 12 13.5 7.5 11.727 7.5 9s2.113-4.5 4.5-4.5zm0 16.5a9.26 9.26 0 01-6.75-2.798c.042-2.233 4.5-3.452 6.75-3.452s6.708 1.219 6.75 3.452A9.26 9.26 0 0112 21z" />
</svg>
Connect HuggingFace
</Link>
</div>
)}
</div>
</div>
{/* Account Linking Status */}
<div className="border rounded-lg p-6 mb-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900">Account Linking Status</h3>
<div className={`px-3 py-1 rounded-full text-sm font-medium ${isLinked ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'}`}>
{isLinked ? 'Linked' : 'Not Linked'}
</div>
</div>
{isLinked ? (
<div className="text-center bg-green-50 p-4 rounded-md">
<svg className="w-10 h-10 text-green-500 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<p className="text-green-800 font-medium">Your GitHub and HuggingFace accounts are linked!</p>
<p className="text-sm text-green-700 mt-1">Linked on: {new Date(userSession?.linkedAt || '').toLocaleString()}</p>
<div className="mt-4">
<button
onClick={() => {
if (confirm("Are you sure you want to unlink your accounts?")) {
window.location.href = "/auth/logout?unlink=true";
}
}}
className="text-sm text-red-600 hover:text-red-800 underline"
>
Unlink Accounts
</button>
</div>
</div>
) : (
<div className="text-center">
{hasGitHub && hasHuggingFace ? (
<div className="bg-yellow-50 p-4 rounded-md">
<svg className="w-10 h-10 text-yellow-500 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<p className="text-yellow-800 font-medium">Your accounts are not linked</p>
<p className="text-sm text-yellow-700 mt-1">You have both accounts connected but they couldn't be linked automatically.</p>
<p className="text-sm text-yellow-700 mt-1">This may be because one of these accounts is already linked to another account.</p>
<div className="mt-4">
<button
onClick={() => {
if (confirm("Try to link your accounts? This will refresh your authentication.")) {
window.location.href = "/auth/github?link=true";
}
}}
className="text-sm text-blue-600 hover:text-blue-800 underline"
>
Try Manual Linking
</button>
</div>
</div>
) : (
<div className="bg-gray-50 p-4 rounded-md">
<p className="text-gray-700">
Connect both your GitHub and HuggingFace accounts to link them.
</p>
</div>
)}
</div>
)}
</div>
{/* Stats */}
{linkingStats && (
<div className="text-center text-sm text-gray-500 mt-4">
<p>Total linked accounts: {linkingStats.totalLinks}</p>
<p>Last updated: {new Date(linkingStats.lastModified).toLocaleString()}</p>
</div>
)}
</div>
</div>
</div>
</div>
);
}