CoolFace
Apppublic

Leon4gr45/builder

sourceHugging Facemitupdated 2d agoView on Hugging Face
0likes
github-auth-panel.tsx195 linesDownload Raw Back to settings
1'use client';2 3import React, { useState, useEffect, useCallback } from 'react';4import { Button } from '@/components/ui/button';5import { Input } from '@/components/ui/input';6import { Label } from '@/components/ui/label';7import { ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react';8import { ConnectionBadge } from '@/components/settings/connection-badge';9import { toast } from 'sonner';10import { configManager } from '@/lib/config/storage';11import { track } from '@/lib/telemetry';12import { apiFetch } from '@/lib/api/backend-status';13 14interface GitHubAuthPanelProps {15  onAuthChange?: () => void;16}17 18export function GitHubAuthPanel({ onAuthChange }: GitHubAuthPanelProps) {19  const [tokenInput, setTokenInput] = useState('');20  const [showToken, setShowToken] = useState(false);21  const [isConnecting, setIsConnecting] = useState(false);22  const [isLoading, setIsLoading] = useState(false);23  const [isConnected, setIsConnected] = useState(() => !!configManager.getGitHubAuth());24  const [username, setUsername] = useState<string | undefined>(() => configManager.getGitHubAuth()?.username);25 26  const dispatchAuthEvent = useCallback((hasKey: boolean) => {27    onAuthChange?.();28    window.dispatchEvent(new CustomEvent('apiKeyUpdated', {29      detail: { provider: 'github', hasKey }30    }));31  }, [onAuthChange]);32 33  useEffect(() => {34    const handleAuthUpdate = (e: Event) => {35      const detail = (e as CustomEvent).detail;36      if (detail?.provider === 'github') {37        const auth = configManager.getGitHubAuth();38        setIsConnected(detail.hasKey);39        if (auth?.username) setUsername(auth.username);40      }41    };42    window.addEventListener('apiKeyUpdated', handleAuthUpdate);43    return () => window.removeEventListener('apiKeyUpdated', handleAuthUpdate);44  }, []);45 46  const handleConnect = async () => {47    const key = tokenInput.trim();48    if (!key) {49      toast.error('Please enter a Personal Access Token');50      return;51    }52    setIsConnecting(true);53    try {54      const response = await apiFetch('/api/validate-key', {55        method: 'POST',56        headers: { 'Content-Type': 'application/json' },57        body: JSON.stringify({ apiKey: key, provider: 'github' })58      });59 60      if (response.ok) {61        const { valid, username: githubUsername } = await response.json();62        if (valid) {63          configManager.setGitHubAuth({ access_token: key, username: githubUsername });64          setIsConnected(true);65          setUsername(githubUsername);66          setTokenInput('');67          toast.success(`Connected to GitHub as ${githubUsername}`);68          track('connection_added', { provider: 'github' });69          dispatchAuthEvent(true);70        } else {71          toast.error('Invalid token. Check that it has "repo" scope.');72        }73      } else {74        toast.error('Failed to validate token');75      }76    } catch {77      toast.error('Failed to validate token. Please try again.');78    } finally {79      setIsConnecting(false);80    }81  };82 83  const handleDisconnect = () => {84    setIsLoading(true);85    try {86      configManager.clearGitHubAuth();87      setIsConnected(false);88      setUsername(undefined);89      setTokenInput('');90      toast.success('Disconnected from GitHub');91      track('connection_removed', { provider: 'github' });92      dispatchAuthEvent(false);93    } catch {94      toast.error('Failed to disconnect');95    } finally {96      setIsLoading(false);97    }98  };99 100  if (isConnected) {101    return (102      <div className="space-y-3">103        <ConnectionBadge104          method="Personal Access Token"105          extra={username}106          info={107            <>108              Authenticated as {username}.{' '}109              <a110                href="https://github.com/settings/tokens"111                target="_blank"112                rel="noopener noreferrer"113                className="text-primary hover:underline inline-flex items-center gap-0.5"114              >115                Manage Tokens <ExternalLink className="h-2.5 w-2.5" />116              </a>117            </>118          }119          onDisconnect={handleDisconnect}120          disconnecting={isLoading}121        />122      </div>123    );124  }125 126  return (127    <div className="space-y-3">128      <Label>GitHub Authentication</Label>129      <p className="text-xs text-muted-foreground">130        Connect your GitHub account to export projects directly to repositories.131      </p>132 133      <div>134        <Label htmlFor="gh-token" className="text-xs">Personal Access Token</Label>135        <div className="flex gap-2 mt-1.5">136          <div className="relative flex-1">137            <Input138              id="gh-token"139              type={showToken ? 'text' : 'password'}140              value={tokenInput}141              onChange={(e) => setTokenInput(e.target.value)}142              onKeyDown={(e) => { if (e.key === 'Enter' && tokenInput.trim()) handleConnect(); }}143              placeholder="ghp_..."144              className="pr-10"145              disabled={isConnecting}146            />147            <Button148              size="icon"149              variant="ghost"150              className="absolute right-1 top-1 h-7 w-7"151              onClick={() => setShowToken(!showToken)}152            >153              {showToken ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}154            </Button>155          </div>156          <Button157            onClick={handleConnect}158            disabled={isConnecting || !tokenInput.trim()}159            size="sm"160          >161            {isConnecting ? (162              <>163                <Loader2 className="h-3 w-3 animate-spin mr-1" />164                Connecting...165              </>166            ) : (167              'Connect'168            )}169          </Button>170        </div>171      </div>172 173      <div className="p-3 border rounded-md bg-muted/50 text-xs text-muted-foreground space-y-2">174        <p className="font-medium">How to get a Personal Access Token:</p>175        <ol className="list-decimal list-inside space-y-1">176          <li>177            Go to{' '}178            <a179              href="https://github.com/settings/tokens"180              target="_blank"181              rel="noopener noreferrer"182              className="text-primary hover:underline inline-flex items-center gap-0.5"183            >184              GitHub Token Settings <ExternalLink className="h-2.5 w-2.5" />185            </a>186          </li>187          <li>Generate a new token (classic or fine-grained)</li>188          <li>Ensure it has <strong>repo</strong> scope (full control of private repositories)</li>189          <li>Paste the token above and click Connect</li>190        </ol>191      </div>192    </div>193  );194}195