anyllm_proxy 0.9.6

HTTP proxy translating Anthropic Messages API to OpenAI Chat Completions
Documentation
import { useEffect, useMemo, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { useKeys } from '../../api/queries'
import AsyncBoundary from '../../components/shared/AsyncBoundary'
import Badge from '../../components/shared/Badge'
import BudgetBar from '../../components/shared/BudgetBar'
import KeyCreateForm from './KeyCreateForm'
import KeyEditModal from './KeyEditModal'
import type { VirtualKey } from '../../api/types'

export default function Keys() {
  const query = useKeys()
  const [params, setParams] = useSearchParams()
  const search = params.get('q') ?? ''
  const editId = params.get('edit')
  const [newKey, setNewKey] = useState<string | null>(null)
  const [editing, setEditing] = useState<VirtualKey | null>(null)

  useEffect(() => {
    if (!editId || !query.data) return
    const match = query.data.find((k) => String(k.id) === editId)
    if (match) setEditing(match)
  }, [editId, query.data])

  function closeEdit() {
    setEditing(null)
    if (params.has('edit')) {
      const next = new URLSearchParams(params)
      next.delete('edit')
      setParams(next, { replace: true })
    }
  }

  function openEdit(k: VirtualKey) {
    setEditing(k)
    const next = new URLSearchParams(params)
    next.set('edit', String(k.id))
    setParams(next, { replace: true })
  }

  function setSearch(value: string) {
    const next = new URLSearchParams(params)
    if (value) next.set('q', value); else next.delete('q')
    setParams(next, { replace: true })
  }

  const filter = search.trim().toLowerCase()
  const filtered = useMemo(() => {
    if (!filter) return query.data ?? []
    return (query.data ?? []).filter((k) => {
      const desc = (k.description ?? '').toLowerCase()
      const prefix = k.key_prefix.toLowerCase()
      return desc.includes(filter) || prefix.includes(filter)
    })
  }, [query.data, filter])

  return (
    <div>
      <KeyCreateForm onCreated={setNewKey} />
      {newKey && (
        <div className="key-result">
          <div className="key-result-label">New key (copy now — not shown again)</div>
          {newKey}
        </div>
      )}
      <div className="toolbar">
        <input
          type="search"
          name="keys-search"
          placeholder="Search by description or prefix…"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          className="toolbar-search"
        />
        {query.data && (
          <span className="dim toolbar-count">
            {filtered.length} of {query.data.length}
          </span>
        )}
      </div>
      <AsyncBoundary
        query={query}
        errorTitle="Failed to load keys"
        empty={{
          when: (keys) => keys.length === 0,
          render: () => (
            <div className="empty-cta">
              <div className="empty-cta-title">No virtual keys yet</div>
              <div className="empty-cta-body">
                Use the form above to create one. Keys are shown once at creation and hashed in storage.
              </div>
            </div>
          ),
        }}
      >
        {() =>
          filtered.length === 0 ? (
            <div className="empty">No keys match "{search}".</div>
          ) : (
            <table className="keys-grid">
              <thead>
                <tr>
                  <th>Prefix</th><th>Description</th><th>Status</th>
                  <th>Spend</th><th>Requests</th><th>Created</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((k) => (
                  <tr key={k.id} style={{ cursor: 'pointer' }} onClick={() => openEdit(k)}>
                    <td className="mono">{k.key_prefix}…</td>
                    <td className="dim">{k.description ?? '—'}</td>
                    <td><Badge variant={k.status} /></td>
                    <td><BudgetBar spent={k.total_spend} limit={k.spend_limit} /></td>
                    <td className="mono">{k.total_requests.toLocaleString()}</td>
                    <td className="mono dim">{k.created_at.slice(0, 10)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </AsyncBoundary>
      {editing && <KeyEditModal key={editing.id} vk={editing} onClose={closeEdit} />}
    </div>
  )
}