From 288120dc1d50ca306c359d905a18614672a9fc8f Mon Sep 17 00:00:00 2001
From: Willie Zutz
Date: Tue, 20 May 2025 00:44:52 -0600
Subject: [PATCH] feat(UI): protect API keys by not displaying them in the UI.
---
src/app/api/config/route.ts | 39 +++++++++++++++++++++++++------------
src/app/settings/page.tsx | 35 +++++++++++++++++++++++----------
2 files changed, 52 insertions(+), 22 deletions(-)
diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts
index e4384a8..1b185c6 100644
--- a/src/app/api/config/route.ts
+++ b/src/app/api/config/route.ts
@@ -51,15 +51,23 @@ export const GET = async (req: Request) => {
});
}
- config['openaiApiKey'] = getOpenaiApiKey();
+ // Helper function to obfuscate API keys
+ const protectApiKey = (key: string | null | undefined) => {
+ return key ? "protected" : key;
+ };
+
+ // Obfuscate all API keys in the response
+ config['openaiApiKey'] = protectApiKey(getOpenaiApiKey());
+ config['groqApiKey'] = protectApiKey(getGroqApiKey());
+ config['anthropicApiKey'] = protectApiKey(getAnthropicApiKey());
+ config['geminiApiKey'] = protectApiKey(getGeminiApiKey());
+ config['deepseekApiKey'] = protectApiKey(getDeepseekApiKey());
+ config['customOpenaiApiKey'] = protectApiKey(getCustomOpenaiApiKey());
+
+ // Non-sensitive values remain unchanged
config['ollamaApiUrl'] = getOllamaApiEndpoint();
config['lmStudioApiUrl'] = getLMStudioApiEndpoint();
- config['anthropicApiKey'] = getAnthropicApiKey();
- config['groqApiKey'] = getGroqApiKey();
- config['geminiApiKey'] = getGeminiApiKey();
- config['deepseekApiKey'] = getDeepseekApiKey();
config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl();
- config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
config['customOpenaiModelName'] = getCustomOpenaiModelName();
config['baseUrl'] = getBaseUrl();
@@ -77,32 +85,39 @@ export const POST = async (req: Request) => {
try {
const config = await req.json();
+ const getUpdatedProtectedValue = (newValue: string, currentConfig: string) => {
+ if (newValue === 'protected') {
+ return currentConfig;
+ }
+ return newValue;
+ }
+
const updatedConfig = {
MODELS: {
OPENAI: {
- API_KEY: config.openaiApiKey,
+ API_KEY: getUpdatedProtectedValue(config.openaiApiKey, getOpenaiApiKey()),
},
GROQ: {
- API_KEY: config.groqApiKey,
+ API_KEY: getUpdatedProtectedValue(config.groqApiKey, getGroqApiKey()),
},
ANTHROPIC: {
- API_KEY: config.anthropicApiKey,
+ API_KEY: getUpdatedProtectedValue(config.anthropicApiKey, getAnthropicApiKey()),
},
GEMINI: {
- API_KEY: config.geminiApiKey,
+ API_KEY: getUpdatedProtectedValue(config.geminiApiKey, getGeminiApiKey()),
},
OLLAMA: {
API_URL: config.ollamaApiUrl,
},
DEEPSEEK: {
- API_KEY: config.deepseekApiKey,
+ API_KEY: getUpdatedProtectedValue(config.deepseekApiKey, getDeepseekApiKey()),
},
LM_STUDIO: {
API_URL: config.lmStudioApiUrl,
},
CUSTOM_OPENAI: {
API_URL: config.customOpenaiApiUrl,
- API_KEY: config.customOpenaiApiKey,
+ API_KEY: getUpdatedProtectedValue(config.customOpenaiApiKey, getCustomOpenaiApiKey()),
MODEL_NAME: config.customOpenaiModelName,
},
},
diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx
index ee78052..aaee479 100644
--- a/src/app/settings/page.tsx
+++ b/src/app/settings/page.tsx
@@ -1,6 +1,6 @@
'use client';
-import { Settings as SettingsIcon, ArrowLeft, Loader2 } from 'lucide-react';
+import { Settings as SettingsIcon, ArrowLeft, Loader2, Info } from 'lucide-react';
import { useEffect, useState } from 'react';
import { cn } from '@/lib/utils';
import { Switch } from '@headlessui/react';
@@ -117,12 +117,24 @@ const Select = ({
const SettingsSection = ({
title,
children,
+ tooltip,
}: {
title: string;
children: React.ReactNode;
+ tooltip?: string;
}) => (
-
{title}
+
+
{title}
+ {tooltip && (
+
+ )}
+
{children}
);
@@ -226,7 +238,7 @@ const Page = () => {
fetchConfig();
}, []);
- const saveConfig = async (key: string, value: any) => {
+ const saveConfig = async (key: string, value: any) => {
setSavingStates((prev) => ({ ...prev, [key]: true }));
try {
@@ -671,7 +683,7 @@ const Page = () => {
Custom OpenAI API Key
{
)}
-
+