diff --git a/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js b/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js
index 836a0ecf..f0a69129 100644
--- a/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js
+++ b/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js
@@ -2,7 +2,7 @@
import { useState, useEffect } from "react";
import PropTypes from "prop-types";
-import { Card, Button, Input, Modal, CardSkeleton, Toggle } from "@/shared/components";
+import { Card, Button, Input, Modal, CardSkeleton, Toggle, AllowedModelsInput } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
/* ========== CLOUD CODE — COMMENTED OUT (replaced by Tunnel) ==========
@@ -24,7 +24,14 @@ export default function APIPageClient({ machineId }) {
const [loading, setLoading] = useState(true);
const [showAddModal, setShowAddModal] = useState(false);
const [newKeyName, setNewKeyName] = useState("");
+ const [newKeyAllowedModels, setNewKeyAllowedModels] = useState([]);
const [createdKey, setCreatedKey] = useState(null);
+ const [editingKey, setEditingKey] = useState(null);
+ const [showEditModal, setShowEditModal] = useState(false);
+ const [editKeyName, setEditKeyName] = useState("");
+ const [editKeyAllowedModels, setEditKeyAllowedModels] = useState([]);
+ const [editKeyIsActive, setEditKeyIsActive] = useState(true);
+ const [apiError, setApiError] = useState(null);
/* ========== CLOUD STATE — COMMENTED OUT (replaced by Tunnel) ==========
const [cloudEnabled, setCloudEnabled] = useState(false);
@@ -328,11 +335,16 @@ export default function APIPageClient({ machineId }) {
const handleCreateKey = async () => {
if (!newKeyName.trim()) return;
+ setApiError(null);
+
try {
const res = await fetch("/api/keys", {
method: "POST",
headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ name: newKeyName }),
+ body: JSON.stringify({
+ name: newKeyName,
+ allowedModels: newKeyAllowedModels.length > 0 ? newKeyAllowedModels : [],
+ }),
});
const data = await res.json();
@@ -340,10 +352,14 @@ export default function APIPageClient({ machineId }) {
setCreatedKey(data.key);
await fetchData();
setNewKeyName("");
+ setNewKeyAllowedModels([]);
setShowAddModal(false);
+ } else {
+ setApiError(data.error || "Failed to create key");
}
} catch (error) {
console.log("Error creating key:", error);
+ setApiError("Failed to create key");
}
};
@@ -381,6 +397,45 @@ export default function APIPageClient({ machineId }) {
}
};
+ const handleEditKey = (key) => {
+ setEditingKey(key);
+ setEditKeyName(key.name);
+ setEditKeyAllowedModels(key.allowedModels || []);
+ setEditKeyIsActive(key.isActive ?? true);
+ setShowEditModal(true);
+ setApiError(null);
+ };
+
+ const handleUpdateKey = async () => {
+ if (!editKeyName.trim() || !editingKey) return;
+
+ setApiError(null);
+
+ try {
+ const res = await fetch(`/api/keys/${editingKey.id}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ name: editKeyName,
+ isActive: editKeyIsActive,
+ allowedModels: editKeyAllowedModels,
+ }),
+ });
+ const data = await res.json();
+
+ if (res.ok) {
+ await fetchData();
+ setShowEditModal(false);
+ setEditingKey(null);
+ } else {
+ setApiError(data.error || "Failed to update key");
+ }
+ } catch (error) {
+ console.log("Error updating key:", error);
+ setApiError("Failed to update key");
+ }
+ };
+
const maskKey = (fullKey) => {
if (!fullKey) return "";
return fullKey.length > 8 ? fullKey.slice(0, 8) + "..." : fullKey;
@@ -395,6 +450,33 @@ export default function APIPageClient({ machineId }) {
});
};
+ const getRestrictionDisplay = (allowedModels) => {
+ if (!allowedModels || allowedModels.length === 0) {
+ return (
+
+ public
+ All Models
+
+ );
+ }
+
+ // Show first 2 patterns + count
+ const display = allowedModels.slice(0, 2).join(", ");
+ const remaining = allowedModels.length - 2;
+
+ return (
+
+ lock
+
+ {display}
+
+ {remaining > 0 && (
+ +{remaining} more
+ )}
+
+ );
+ };
+
const [baseUrl, setBaseUrl] = useState("/v1");
// Hydration fix: Only access window on client side
@@ -549,6 +631,9 @@ export default function APIPageClient({ machineId }) {
+
+ {getRestrictionDisplay(key.allowedModels)}
+
Created {new Date(key.createdAt).toLocaleDateString()}
@@ -571,9 +656,17 @@ export default function APIPageClient({ machineId }) {
}}
title={key.isActive ? "Pause key" : "Resume key"}
/>
+
@@ -595,7 +688,10 @@ export default function APIPageClient({ machineId }) {
onClose={() => {
setShowAddModal(false);
setNewKeyName("");
+ setNewKeyAllowedModels([]);
+ setApiError(null);
}}
+ size="lg"
>
setNewKeyName(e.target.value)}
placeholder="Production Key"
+ required
+ />
+
+
+
+ {apiError && (
+
+ error
+ {apiError}
+
+ )}
+
+
+
+
+
+ {/* Edit Key Modal */}
+ {
+ setShowEditModal(false);
+ setEditingKey(null);
+ setApiError(null);
+ }}
+ size="lg"
+ >
+
+
setEditKeyName(e.target.value)}
+ placeholder="Production Key"
+ required
+ />
+
+
+
+
+
Active
+
+ {editKeyIsActive ? "Key is active and working" : "Key is paused and won't work"}
+
+
+
+
+
+
+ {apiError && (
+
+ error
+ {apiError}
+
+ )}
+
+
+
+