diff --git a/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js b/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js
index 0a6cca8c..5ab015b2 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 } from "@/shared/components";
+import { Card, Button, Input, Modal, CardSkeleton, Toggle } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
const DEFAULT_CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL || "";
@@ -16,6 +16,7 @@ export default function APIPageClient({ machineId }) {
const [createdKey, setCreatedKey] = useState(null);
// Cloud sync state
+ const [requireApiKey, setRequireApiKey] = useState(false);
const [cloudEnabled, setCloudEnabled] = useState(false);
const [cloudUrl, setCloudUrl] = useState(DEFAULT_CLOUD_URL);
const [cloudUrlInput, setCloudUrlInput] = useState(DEFAULT_CLOUD_URL);
@@ -63,6 +64,7 @@ export default function APIPageClient({ machineId }) {
if (res.ok) {
const data = await res.json();
setCloudEnabled(data.cloudEnabled || false);
+ setRequireApiKey(data.requireApiKey || false);
const url = data.cloudUrl || DEFAULT_CLOUD_URL;
setCloudUrl(url);
setCloudUrlInput(url);
@@ -72,6 +74,19 @@ export default function APIPageClient({ machineId }) {
}
};
+ const handleRequireApiKey = async (value) => {
+ try {
+ const res = await fetch("/api/settings", {
+ method: "PATCH",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ requireApiKey: value }),
+ });
+ if (res.ok) setRequireApiKey(value);
+ } catch (error) {
+ console.log("Error updating requireApiKey:", error);
+ }
+ };
+
const fetchData = async () => {
try {
const keysRes = await fetch("/api/keys");
@@ -361,6 +376,19 @@ export default function APIPageClient({ machineId }) {
+
+
+
Require API key
+
+ Requests without a valid key will be rejected
+
+
+
handleRequireApiKey(!requireApiKey)}
+ />
+
+
{keys.length === 0 ? (
diff --git a/src/sse/handlers/chat.js b/src/sse/handlers/chat.js
index cddbaac6..bc7b8c27 100644
--- a/src/sse/handlers/chat.js
+++ b/src/sse/handlers/chat.js
@@ -5,6 +5,7 @@ import {
extractApiKey,
isValidApiKey,
} from "../services/auth.js";
+import { getSettings } from "@/lib/localDb";
import { getModelInfo, getComboModels } from "../services/model.js";
import { handleChatCore } from "open-sse/handlers/chatCore.js";
import { errorResponse, unavailableResponse } from "open-sse/utils/error.js";
@@ -57,16 +58,16 @@ export async function handleChat(request, clientRawRequest = null) {
log.debug("AUTH", "No API key provided (local mode)");
}
- // Optional strict API key mode for /v1 endpoints.
- // Keep disabled by default to preserve local-mode compatibility.
- if (process.env.REQUIRE_API_KEY === "true") {
+ // Enforce API key if enabled in settings
+ const settings = await getSettings();
+ if (settings.requireApiKey) {
if (!apiKey) {
- log.warn("AUTH", "Missing API key while REQUIRE_API_KEY=true");
+ log.warn("AUTH", "Missing API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Missing API key");
}
const valid = await isValidApiKey(apiKey);
if (!valid) {
- log.warn("AUTH", "Invalid API key while REQUIRE_API_KEY=true");
+ log.warn("AUTH", "Invalid API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key");
}
}
diff --git a/src/sse/handlers/embeddings.js b/src/sse/handlers/embeddings.js
index 344df4c3..3449ea58 100644
--- a/src/sse/handlers/embeddings.js
+++ b/src/sse/handlers/embeddings.js
@@ -5,6 +5,7 @@ import {
extractApiKey,
isValidApiKey,
} from "../services/auth.js";
+import { getSettings } from "@/lib/localDb";
import { getModelInfo } from "../services/model.js";
import { handleEmbeddingsCore } from "open-sse/handlers/embeddingsCore.js";
import { errorResponse, unavailableResponse } from "open-sse/utils/error.js";
@@ -40,15 +41,16 @@ export async function handleEmbeddings(request) {
log.debug("AUTH", "No API key provided (local mode)");
}
- // Optional strict API key validation
- if (process.env.REQUIRE_API_KEY === "true") {
+ // Enforce API key if enabled in settings
+ const settings = await getSettings();
+ if (settings.requireApiKey) {
if (!apiKey) {
- log.warn("AUTH", "Missing API key while REQUIRE_API_KEY=true");
+ log.warn("AUTH", "Missing API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Missing API key");
}
const valid = await isValidApiKey(apiKey);
if (!valid) {
- log.warn("AUTH", "Invalid API key while REQUIRE_API_KEY=true");
+ log.warn("AUTH", "Invalid API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key");
}
}