feat: implement API key requirement toggle

This commit is contained in:
decolua
2026-02-18 13:46:14 +07:00
parent b057c43c27
commit 4cf25dc53d
3 changed files with 41 additions and 10 deletions

View File

@@ -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 }) {
</Button>
</div>
<div className="flex items-center justify-between pb-4 mb-4 border-b border-border">
<div>
<p className="font-medium">Require API key</p>
<p className="text-sm text-text-muted">
Requests without a valid key will be rejected
</p>
</div>
<Toggle
checked={requireApiKey}
onChange={() => handleRequireApiKey(!requireApiKey)}
/>
</div>
{keys.length === 0 ? (
<div className="text-center py-12">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 text-primary mb-4">

View File

@@ -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");
}
}

View File

@@ -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");
}
}