import Link from "next/link"; import { createApiKey, deleteApiKey, toggleApiKey } from "@/actions/admin-radio-api-keys"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; import { Key } from "lucide-react"; export const dynamic = "force-dynamic"; export const metadata = { title: "Radio API Keys" }; type ApiKey = { id: bigint; name: string; key: string; allowedIps: string | null; rateLimit: number; lastUsedAt: Date | null; expiresAt: Date | null; isActive: boolean; createdAt: Date | null; }; function formatDate(d: Date | null): string { return d ? d.toISOString().slice(0, 16).replace("T", " ") : "—"; } /** Show only the first 8 chars of the secret; the rest stays masked. */ function maskKey(key: string): string { return key.length > 8 ? `${key.slice(0, 8)}…` : key; } export default async function AdminRadioApiKeysPage() { let keys: ApiKey[] = []; let dbError = false; try { keys = await prisma.radioApiKeys .findMany({ select: { id: true, name: true, key: true, allowedIps: true, rateLimit: true, lastUsedAt: true, expiresAt: true, isActive: true, createdAt: true, }, orderBy: [{ isActive: "desc" }, { id: "desc" }], take: 500, }) .catch(() => { dbError = true; return []; }); } catch { dbError = true; keys = []; } const total = keys.length; const activeCount = keys.filter((k) => k.isActive).length; const inactiveCount = total - activeCount; return (

Radio API Keys

API keys for external radio integrations (radio_api_keys). Keys are generated server-side and shown masked — copy the full value from the database when first issued.

{dbError ? (

Could not load API keys (database unavailable).

) : (
0 ? "ok" : "neutral"} icon="✅" /> 0 ? "warn" : "neutral"} icon="🚫" />
)}

Generate key

The secret key is generated automatically with cryptographic randomness — you do not enter it.

Existing keys ({total})

{total === 0 ? (
No API keys yet — generate one above.
) : (
{keys.map((k) => ( ))}
# Name Key Allowed IPs Rate limit Last used Expires Status Actions
{String(k.id)} {k.name} {maskKey(k.key)} {k.allowedIps || "any"} {k.rateLimit}/min {formatDate(k.lastUsedAt)} {formatDate(k.expiresAt)} {k.isActive ? "active" : "inactive"}
)}
); }