File size: 3,589 Bytes
fc9bd9f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import React, { useState } from "react";
import { AlertCircle, ExternalLink, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useApi } from "@/contexts/ApiContext";
import { useHfAuth } from "@/contexts/HfAuthContext";

const HfAuthBanner: React.FC = () => {
  const { auth, refetch } = useHfAuth();
  const { baseUrl, fetchWithHeaders } = useApi();
  const [token, setToken] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  if (auth.status === "authenticated" || auth.status === "loading") {
    return null;
  }

  const handleSave = async () => {
    const trimmed = token.trim();
    if (!trimmed) return;
    setSubmitting(true);
    setError(null);
    try {
      const r = await fetchWithHeaders(`${baseUrl}/hf-auth/login`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token: trimmed }),
      });
      if (!r.ok) {
        const body = await r.json().catch(() => ({}));
        throw new Error(body.detail || `HTTP ${r.status}`);
      }
      setToken("");
      await refetch();
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="bg-amber-950/40 border border-amber-700/60 rounded-lg p-4 mb-6">
      <div className="flex items-start gap-3">
        <AlertCircle className="w-5 h-5 text-amber-400 flex-shrink-0 mt-0.5" />
        <div className="flex-1 space-y-3">
          <div>
            <p className="text-sm text-amber-100 font-medium">
              Hugging Face access required for cloud training
            </p>
            <p className="text-xs text-amber-200/80 mt-1">
              Create a token at{" "}
              <a
                href="https://huggingface.co/settings/tokens"
                target="_blank"
                rel="noreferrer"
                className="underline hover:text-amber-50 inline-flex items-center gap-1"
              >
                huggingface.co/settings/tokens
                <ExternalLink className="w-3 h-3" />
              </a>
              {" "}with <span className="font-mono">Write</span> access (so trained
              policies can upload to your account), then paste it below.
            </p>
          </div>
          <form
            onSubmit={(e) => {
              e.preventDefault();
              handleSave();
            }}
            className="flex gap-2"
          >
            <Input
              type="password"
              placeholder="hf_..."
              value={token}
              onChange={(e) => setToken(e.target.value)}
              className="bg-slate-900 border-slate-600 text-white placeholder:text-slate-500"
              disabled={submitting}
              autoComplete="off"
            />
            <Button
              type="submit"
              disabled={submitting || !token.trim()}
              className="bg-amber-600 hover:bg-amber-700 text-white"
            >
              {submitting ? (
                <>
                  <Loader2 className="w-4 h-4 mr-2 animate-spin" />
                  Saving…
                </>
              ) : (
                "Save token"
              )}
            </Button>
          </form>
          {error && (
            <p className="text-xs text-red-300">{error}</p>
          )}
        </div>
      </div>
    </div>
  );
};

export default HfAuthBanner;