'use client' import { useState, useEffect, useCallback } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { useToast } from '@/components/ui/use-toast' import { Loader2, Plus, Copy, Check, Trash2, Key } from 'lucide-react' interface ApiKey { id: string key_prefix: string name: string scopes: string[] | null rate_limit_rpm: number last_used_at: string | null revoked_at: string | null created_at: string } export function ApiKeysPanel() { const { toast } = useToast() const [keys, setKeys] = useState([]) const [isLoading, setIsLoading] = useState(true) const [isCreating, setIsCreating] = useState(false) const [showCreateDialog, setShowCreateDialog] = useState(false) const [showKeyDialog, setShowKeyDialog] = useState(false) const [newKeyName, setNewKeyName] = useState('') const [newKeyValue, setNewKeyValue] = useState('') const [copied, setCopied] = useState(false) const [revokingId, setRevokingId] = useState(null) const fetchKeys = useCallback(async () => { try { const res = await fetch('/api/settings/api-keys') const json = await res.json() if (json.data) { setKeys(json.data.filter((k: ApiKey) => !k.revoked_at)) } } catch { toast({ title: 'Fel', description: 'Kunde inte hämta API-nycklar', variant: 'destructive' }) } finally { setIsLoading(false) } }, [toast]) useEffect(() => { fetchKeys() }, [fetchKeys]) async function handleCreate() { setIsCreating(true) try { const res = await fetch('/api/settings/api-keys', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newKeyName || 'MCP-nyckel' }), }) const json = await res.json() if (!res.ok) { toast({ title: 'Fel', description: json.error, variant: 'destructive' }) return } setNewKeyValue(json.data.key) setShowCreateDialog(false) setShowKeyDialog(true) setNewKeyName('') fetchKeys() } catch { toast({ title: 'Fel', description: 'Kunde inte skapa nyckel', variant: 'destructive' }) } finally { setIsCreating(false) } } async function handleRevoke(id: string) { setRevokingId(id) try { await fetch(`/api/settings/api-keys/${id}`, { method: 'DELETE' }) setKeys((prev) => prev.filter((k) => k.id !== id)) toast({ title: 'Nyckel återkallad' }) } catch { toast({ title: 'Fel', description: 'Kunde inte återkalla nyckel', variant: 'destructive' }) } finally { setRevokingId(null) } } function handleCopy() { navigator.clipboard.writeText(newKeyValue) setCopied(true) setTimeout(() => setCopied(false), 2000) } function formatDate(iso: string | null) { if (!iso) return '—' return new Date(iso).toLocaleDateString('sv-SE', { year: 'numeric', month: 'short', day: 'numeric', }) } const mcpUrl = typeof window !== 'undefined' ? `${window.location.origin}/api/extensions/ext/mcp-server/mcp` : '/api/extensions/ext/mcp-server/mcp' return (
API-nycklar Hantera nycklar för MCP-klienter (Claude, Cursor) och andra integrationer.
{isLoading ? (
) : keys.length === 0 ? (

Inga API-nycklar ännu.

Skapa en nyckel för att koppla din MCP-klient.

) : (
{keys.map((key) => (

{key.name}

{key.key_prefix}... Skapad {formatDate(key.created_at)} {key.last_used_at ? `Använd ${formatDate(key.last_used_at)}` : 'Aldrig använd'}
))}
)}
Anslut MCP-klient

Claude Desktop

Lägg till i claude_desktop_config.json (Inställningar → Developer):

{`{
  "mcpServers": {
    "gnubok": {
      "command": "npx",
      "args": ["gnubok-mcp"],
      "env": {
        "GNUBOK_API_KEY": "gnubok_sk_..."
      }
    }
  }
}`}
            

Claude Code / Cursor

Kör i terminalen med en API-nyckel:

{`claude mcp add gnubok --transport http \\
  --url ${mcpUrl} \\
  --header "Authorization: Bearer gnubok_sk_..."`}
            
{/* Create key dialog */} Skapa API-nyckel Ge nyckeln ett namn så du vet vad den används till.
setNewKeyName(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleCreate()} />
{/* Show key once dialog */} { if (!open) { setNewKeyValue('') setCopied(false) } setShowKeyDialog(open) }}> Din nya API-nyckel Kopiera nyckeln nu. Den visas bara en gång.
{newKeyValue}
) }