diff --git a/bun.lock b/bun.lock index cabd7313..259abd31 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "chalk": "^5.6.2", "chalk-animation": "^2.0.3", "long": "^5.3.2", + "zod": "^4.3.6", }, "devDependencies": { "@ai-sdk/mcp": "^1.0.13", diff --git a/frontend/src/features/workflow-builder/hooks/useWorkflowImportExport.ts b/frontend/src/features/workflow-builder/hooks/useWorkflowImportExport.ts index fa9a2828..32e1dfd8 100644 --- a/frontend/src/features/workflow-builder/hooks/useWorkflowImportExport.ts +++ b/frontend/src/features/workflow-builder/hooks/useWorkflowImportExport.ts @@ -12,6 +12,8 @@ import { } from '@/features/workflow-builder/hooks/useWorkflowGraphControllers'; import type { FrontendNodeData } from '@/schemas/node'; import type { Node as ReactFlowNode, Edge as ReactFlowEdge } from 'reactflow'; +import { useSecretStore } from '@/store/secretStore'; +import { useComponentStore } from '@/store/componentStore'; interface WorkflowMetadataShape { id: string | null; name: string; @@ -98,6 +100,54 @@ export function useWorkflowImportExport({ const normalizedNodes = deserializeNodes(workflowGraph); const normalizedEdges = deserializeEdges(workflowGraph); + // Validate secret references + const removedSecrets: { param: string; node: string; secretId: string }[] = []; + try { + await useSecretStore.getState().fetchSecrets(); + const secrets = useSecretStore.getState().secrets; + const secretIds = new Set(secrets.map((s) => s.id)); + + const componentStore = useComponentStore.getState(); + if (Object.keys(componentStore.components).length === 0) { + await componentStore.fetchComponents(); + } + const components = useComponentStore.getState().components; + + normalizedNodes.forEach((node) => { + const data = node.data as FrontendNodeData; + const componentRef = data.componentId || data.componentSlug; + if (!componentRef) return; + + const component = + componentStore.getComponent(componentRef) || + Object.values(components).find((c) => c.slug === componentRef); + + if (!component || !component.parameters) return; + + // Find parameters that are secrets + const secretParams = component.parameters.filter((p) => p.type === 'secret'); + const configParams = node.data.config.params || {}; + + secretParams.forEach((param) => { + const val = configParams[param.id]; + // If value is a string (ID) and not in available secrets, remove it + if (typeof val === 'string' && val.trim().length > 0) { + if (!secretIds.has(val)) { + console.warn( + `[Import] Removing invalid secret reference for param "${param.id}" in node "${node.id}" (secret ID: ${val})`, + ); + removedSecrets.push({ param: param.id, node: node.id, secretId: val }); + // Set to undefined to clear it + configParams[param.id] = undefined; + } + } + }); + }); + } catch (error) { + console.error('Failed to validate secrets during import:', error); + // Continue with import even if validation fails + } + resetWorkflow(); setDesignNodes(normalizedNodes); setDesignEdges(normalizedEdges); @@ -118,6 +168,15 @@ export function useWorkflowImportExport({ title: 'Workflow imported', description: `Loaded ${parsed.name}`, }); + + // Show warning if any invalid secret references were removed + if (removedSecrets.length > 0) { + toast({ + variant: 'warning', + title: 'Invalid secret references removed', + description: `${removedSecrets.length} secret reference(s) could not be resolved and were cleared. Please select valid secrets from the Secrets Manager.`, + }); + } }, [ canManageWorkflows, diff --git a/package.json b/package.json index 1b6e8dc3..6c8621c2 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ "undici": "^7.19.0" }, "lint-staged": { - "frontend/**/*.{ts,tsx,js,jsx}": "bunx eslint --fix --config frontend/eslint.config.mjs", - "backend/**/*.{ts,js}": "bunx eslint --fix --config backend/eslint.config.mjs", - "worker/**/*.{ts,js}": "bunx eslint --fix --config worker/eslint.config.mjs", + "frontend/**/*.{ts,tsx,js,jsx}": "bunx eslint@9 --fix --config frontend/eslint.config.mjs", + "backend/**/*.{ts,js}": "bunx eslint@9 --fix --config backend/eslint.config.mjs", + "worker/**/*.{ts,js}": "bunx eslint@9 --fix --config worker/eslint.config.mjs", "*.{json,md,yml,yaml}": "bunx prettier --write" }, "dependencies": { @@ -61,6 +61,7 @@ "@grpc/grpc-js": "^1.14.3", "chalk": "^5.6.2", "chalk-animation": "^2.0.3", - "long": "^5.3.2" + "long": "^5.3.2", + "zod": "^4.3.6" } }