Skip to content

Commit 3f0477f

Browse files
Merge pull request #2 from JunSuzuki1973/fix/user-specific-api-keys
Fix: Use user-specific API keys for workflow execution
2 parents fab1785 + 4e856f4 commit 3f0477f

File tree

4 files changed

+57
-13
lines changed

4 files changed

+57
-13
lines changed

app/api/workflows/[workflowId]/execute-langgraph/route.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
22
import { LangGraphExecutor } from '@/lib/workflow/langgraph';
33
import { getWorkflow } from '@/lib/workflow/storage';
44
import { getServerAPIKeys } from '@/lib/api/config';
5+
import { validateApiKey } from '@/lib/api/auth';
56

67
export const dynamic = 'force-dynamic';
78

@@ -14,6 +15,15 @@ export async function POST(
1415
{ params }: { params: Promise<{ workflowId: string }> }
1516
) {
1617
try {
18+
// Validate API key
19+
const authResult = await validateApiKey(request);
20+
if (!authResult.authenticated) {
21+
return NextResponse.json(
22+
{ error: authResult.error || 'Authentication required' },
23+
{ status: 401 }
24+
);
25+
}
26+
1727
const { workflowId } = await params;
1828
const body = await request.json();
1929
const { input, threadId } = body;
@@ -27,8 +37,17 @@ export async function POST(
2737
);
2838
}
2939

30-
// Get API keys
31-
const apiKeys = getServerAPIKeys();
40+
// Get API keys - check user keys first, then fall back to environment
41+
const { getLLMApiKey } = await import('@/lib/api/llm-keys');
42+
const userId = authResult.userId;
43+
44+
const apiKeys = {
45+
anthropic: userId ? await getLLMApiKey('anthropic', userId) : null || process.env.ANTHROPIC_API_KEY,
46+
groq: userId ? await getLLMApiKey('groq', userId) : null || process.env.GROQ_API_KEY,
47+
openai: userId ? await getLLMApiKey('openai', userId) : null || process.env.OPENAI_API_KEY,
48+
firecrawl: process.env.FIRECRAWL_API_KEY, // Firecrawl keys are still environment-only for now
49+
arcade: process.env.ARCADE_API_KEY,
50+
};
3251

3352
// Create LangGraph executor
3453
const executor = new LangGraphExecutor(workflow, undefined, apiKeys || undefined);

app/api/workflows/[workflowId]/execute-stream/route.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,15 @@ export async function POST(
107107
const executionId = `exec_${Date.now()}`;
108108
const nodeResults: Record<string, any> = {};
109109

110-
// Get API keys
110+
// Get API keys - check user keys first, then fall back to environment
111+
const { getLLMApiKey } = await import('@/lib/api/llm-keys');
112+
const userId = authResult.userId;
113+
111114
const apiKeys = {
112-
anthropic: process.env.ANTHROPIC_API_KEY,
113-
groq: process.env.GROQ_API_KEY,
114-
openai: process.env.OPENAI_API_KEY,
115-
firecrawl: process.env.FIRECRAWL_API_KEY,
115+
anthropic: userId ? await getLLMApiKey('anthropic', userId) : null || process.env.ANTHROPIC_API_KEY,
116+
groq: userId ? await getLLMApiKey('groq', userId) : null || process.env.GROQ_API_KEY,
117+
openai: userId ? await getLLMApiKey('openai', userId) : null || process.env.OPENAI_API_KEY,
118+
firecrawl: process.env.FIRECRAWL_API_KEY, // Firecrawl keys are still environment-only for now
116119
arcade: process.env.ARCADE_API_KEY,
117120
};
118121

app/api/workflows/[workflowId]/resume/route.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,15 @@ export async function POST(
8080
id: workflowDoc.customId || workflowDoc._id,
8181
} as any;
8282

83-
// Get API keys
83+
// Get API keys - check user keys first, then fall back to environment
84+
const { getLLMApiKey } = await import('@/lib/api/llm-keys');
85+
const userId = authResult.userId;
86+
8487
const apiKeys = {
85-
anthropic: process.env.ANTHROPIC_API_KEY,
86-
groq: process.env.GROQ_API_KEY,
87-
openai: process.env.OPENAI_API_KEY,
88-
firecrawl: process.env.FIRECRAWL_API_KEY,
88+
anthropic: userId ? await getLLMApiKey('anthropic', userId) : null || process.env.ANTHROPIC_API_KEY,
89+
groq: userId ? await getLLMApiKey('groq', userId) : null || process.env.GROQ_API_KEY,
90+
openai: userId ? await getLLMApiKey('openai', userId) : null || process.env.OPENAI_API_KEY,
91+
firecrawl: process.env.FIRECRAWL_API_KEY, // Firecrawl keys are still environment-only for now
8992
arcade: process.env.ARCADE_API_KEY,
9093
};
9194

components/app/(home)/sections/workflow-builder/WorkflowBuilder.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import DataNodePanel from "./DataNodePanel";
4646
import ToolsNodePanel from "./ToolsNodePanel";
4747
import NoteNodePanel from "./NoteNodePanel";
4848
import HTTPNodePanel from "./HTTPNodePanel";
49+
import ExtractNodePanel from "./ExtractNodePanel";
4950
import StartNodePanel from "./StartNodePanel";
5051
import WorkflowNameEditor from "./WorkflowNameEditor";
5152
import SettingsPanel from "./SettingsPanelSimple";
@@ -138,6 +139,8 @@ const nodeCategories = [
138139
category: "Data",
139140
nodes: [
140141
{ type: "transform", label: "Transform", color: "bg-[#ECE3FF] dark:bg-[#9665FF]", icon: Braces },
142+
{ type: "extract", label: "Extract", color: "bg-[#ECE3FF] dark:bg-[#9665FF]", icon: Search },
143+
{ type: "http", label: "HTTP", color: "bg-[#ECE3FF] dark:bg-[#9665FF]", icon: Server },
141144
{ type: "set-state", label: "Set state", color: "bg-[#ECE3FF] dark:bg-[#9665FF]", icon: Braces },
142145
],
143146
},
@@ -1566,7 +1569,7 @@ function WorkflowBuilderInner({ onBack, initialWorkflowId, initialTemplateId }:
15661569
initial={{ x: -300, opacity: 0 }}
15671570
animate={{ x: 0, opacity: 1 }}
15681571
transition={{ duration: 0.5, delay: 0.2 }}
1569-
className="w-200 lg:w-200 md:w-180 sm:w-160 m-20 rounded-16 border border-border-faint bg-accent-white p-16 shadow-lg flex-shrink-0 z-10 self-start"
1572+
className="w-200 lg:w-200 md:w-180 sm:w-160 m-20 rounded-16 border border-border-faint bg-accent-white p-16 shadow-lg flex-shrink-0 z-10 self-start max-h-[calc(100vh-80px)] overflow-y-auto"
15701573
>
15711574
<div className="mb-24">
15721575
<button
@@ -1727,6 +1730,22 @@ function WorkflowBuilderInner({ onBack, initialWorkflowId, initialTemplateId }:
17271730
onDelete={handleDeleteNode}
17281731
onUpdate={handleUpdateNodeData}
17291732
/>
1733+
) : (selectedNode?.data as any)?.nodeType === 'extract' ? (
1734+
<ExtractNodePanel
1735+
node={selectedNode}
1736+
nodes={nodes}
1737+
onClose={() => setSelectedNode(null)}
1738+
onDelete={handleDeleteNode}
1739+
onUpdate={handleUpdateNodeData}
1740+
/>
1741+
) : (selectedNode?.data as any)?.nodeType === 'http' ? (
1742+
<HTTPNodePanel
1743+
node={selectedNode}
1744+
nodes={nodes}
1745+
onClose={() => setSelectedNode(null)}
1746+
onDelete={handleDeleteNode}
1747+
onUpdate={handleUpdateNodeData}
1748+
/>
17301749
) : (selectedNode?.data as any)?.nodeType?.includes('set-state') ? (
17311750
<DataNodePanel
17321751
node={selectedNode}

0 commit comments

Comments
 (0)