From b6c77431e720455357ce2aed05cfb83df8fe852b Mon Sep 17 00:00:00 2001
From: lily-de <119957291+lily-de@users.noreply.github.com>
Date: Sun, 26 Jan 2025 18:11:23 -0500
Subject: [PATCH] fix:dont allow builtin deletion (#797)
---
.../src/components/settings/Settings.tsx | 40 +++--
.../ConfigureBuiltInExtensionModal.tsx | 162 ++++++++++++++++++
.../settings/extensions/ExtensionItem.tsx | 11 +-
ui/desktop/src/extensions.tsx | 2 +-
4 files changed, 199 insertions(+), 16 deletions(-)
create mode 100644 ui/desktop/src/components/settings/extensions/ConfigureBuiltInExtensionModal.tsx
diff --git a/ui/desktop/src/components/settings/Settings.tsx b/ui/desktop/src/components/settings/Settings.tsx
index 2b81baa8..8a26859e 100644
--- a/ui/desktop/src/components/settings/Settings.tsx
+++ b/ui/desktop/src/components/settings/Settings.tsx
@@ -11,6 +11,7 @@ import {
} from '../../extensions';
import { ConfigureExtensionModal } from './extensions/ConfigureExtensionModal';
import { ManualExtensionModal } from './extensions/ManualExtensionModal';
+import { ConfigureBuiltInExtensionModal } from './extensions/ConfigureBuiltInExtensionModal';
import BackButton from '../ui/BackButton';
import { RecentModelsRadio } from './models/RecentModels';
import { ExtensionItem } from './extensions/ExtensionItem';
@@ -167,6 +168,10 @@ export default function Settings() {
navigate('/settings', { replace: true });
};
+ const isBuiltIn = (extensionId: string) => {
+ return BUILT_IN_EXTENSIONS.some((builtIn) => builtIn.id === extensionId);
+ };
+
return (
@@ -232,6 +237,7 @@ export default function Settings() {
setExtensionBeingConfigured(extension)}
/>
@@ -244,17 +250,29 @@ export default function Settings() {
- {
- setExtensionBeingConfigured(null);
- // Clear URL parameters when closing manually
- navigate('/settings', { replace: true });
- }}
- extension={extensionBeingConfigured}
- onSubmit={handleExtensionConfigSubmit}
- onRemove={handleExtensionRemove}
- />
+ {extensionBeingConfigured && isBuiltIn(extensionBeingConfigured.id) ? (
+ {
+ setExtensionBeingConfigured(null);
+ navigate('/settings', { replace: true });
+ }}
+ extension={extensionBeingConfigured}
+ onSubmit={handleExtensionConfigSubmit}
+ />
+ ) : (
+ {
+ setExtensionBeingConfigured(null);
+ // Clear URL parameters when closing manually
+ navigate('/settings', { replace: true });
+ }}
+ extension={extensionBeingConfigured}
+ onSubmit={handleExtensionConfigSubmit}
+ onRemove={handleExtensionRemove}
+ />
+ )}
void;
+ onSubmit: () => void;
+ extension: FullExtensionConfig | null;
+}
+
+export function ConfigureBuiltInExtensionModal({
+ isOpen,
+ onClose,
+ onSubmit,
+ extension,
+}: ConfigureExtensionModalProps) {
+ const [envValues, setEnvValues] = React.useState>({});
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
+
+ // Reset form when dialog closes or extension changes
+ React.useEffect(() => {
+ if (!isOpen || !extension) {
+ setEnvValues({});
+ }
+ }, [isOpen, extension]);
+
+ const handleExtensionConfigSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!extension) return;
+
+ setIsSubmitting(true);
+ try {
+ // First store all environment variables
+ if (extension.env_keys?.length > 0) {
+ for (const envKey of extension.env_keys) {
+ const value = envValues[envKey];
+ if (!value) continue;
+
+ const storeResponse = await fetch(getApiUrl('/secrets/store'), {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Secret-Key': getSecretKey(),
+ },
+ body: JSON.stringify({
+ key: envKey,
+ value: value.trim(),
+ }),
+ });
+
+ if (!storeResponse.ok) {
+ throw new Error(`Failed to store environment variable: ${envKey}`);
+ }
+ }
+ }
+
+ const response = await addExtension(extension);
+
+ if (!response.ok) {
+ throw new Error('Failed to add system configuration');
+ }
+
+ toast.success(`Successfully configured the ${extension.name} extension`);
+ onSubmit();
+ onClose();
+ } catch (error) {
+ console.error('Error configuring extension:', error);
+ toast.error('Failed to configure extension');
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ if (!extension || !isOpen) return null;
+
+ return (
+
+
+
+ {/* Header */}
+
+
+ Configure {extension.name}
+
+
+
+ {/* Form */}
+
+
+
+
+ );
+}
diff --git a/ui/desktop/src/components/settings/extensions/ExtensionItem.tsx b/ui/desktop/src/components/settings/extensions/ExtensionItem.tsx
index a9f06c7d..36c184d4 100644
--- a/ui/desktop/src/components/settings/extensions/ExtensionItem.tsx
+++ b/ui/desktop/src/components/settings/extensions/ExtensionItem.tsx
@@ -5,10 +5,11 @@ import { Gear } from '../../icons';
type ExtensionItemProps = FullExtensionConfig & {
onToggle: (id: string) => void;
onConfigure: (extension: FullExtensionConfig) => void;
+ canConfigure?: boolean; // Added optional prop here
};
export const ExtensionItem: React.FC = (props) => {
- const { id, name, description, enabled, onToggle, onConfigure } = props;
+ const { id, name, description, enabled, onToggle, onConfigure, canConfigure } = props;
return (
@@ -20,9 +21,11 @@ export const ExtensionItem: React.FC
= (props) => {
{description}
-
+ {canConfigure && ( // Conditionally render the gear icon
+
+ )}