Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/components/flow/detail-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useCallback, useMemo } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useParams } from "next/navigation";
import { Copy, Trash2, Lock, Info } from "lucide-react";
import { useFlowStore } from "@/stores/flow-store";
Expand Down Expand Up @@ -122,6 +122,13 @@
? nodes.find((n) => n.id === selectedNodeId)
: null;

const storeKey = (selectedNode?.data as { componentKey?: string })?.componentKey ?? "";
const [displayKey, setDisplayKey] = useState(storeKey);

useEffect(() => {
setDisplayKey(storeKey);
}, [storeKey]);

const upstream = useMemo(
() =>
selectedNodeId
Expand All @@ -140,12 +147,21 @@
);

const handleKeyChange = useCallback(
(key: string) => {
(raw: string) => {
if (selectedNodeId) {
updateNodeKey(selectedNodeId, key);
const sanitized = raw
.replace(/\s+/g, "_")
.replace(/[^a-zA-Z0-9_]/g, "")
.replace(/^(\d+)/, "_$1");
if (sanitized) {
setDisplayKey(raw);
updateNodeKey(selectedNodeId, sanitized);
} else {
setDisplayKey(storeKey);
}
}
Comment on lines 151 to 162
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty string written to store when all characters are stripped

If the user types a value composed entirely of characters that are removed by the second replace step (e.g. "!!!"), sanitized becomes "" and updateNodeKey(selectedNodeId, "") is called. This stores an empty component key, which will silently fail Zod validation only at save time, potentially leaving the user with a broken node state.

Adding a guard prevents this silent bad state:

Suggested change
if (selectedNodeId) {
updateNodeKey(selectedNodeId, key);
const sanitized = raw
.replace(/\s+/g, "_")
.replace(/[^a-zA-Z0-9_]/g, "")
.replace(/^(\d+)/, "_$1");
updateNodeKey(selectedNodeId, sanitized);
}
const sanitized = raw
.replace(/\s+/g, "_")
.replace(/[^a-zA-Z0-9_]/g, "")
.replace(/^(\d+)/, "_$1");
if (sanitized) updateNodeKey(selectedNodeId, sanitized);

},
[selectedNodeId, updateNodeKey],
[selectedNodeId, updateNodeKey, storeKey],
);

const handleDelete = useCallback(() => {
Expand Down Expand Up @@ -204,7 +220,7 @@
);
}

const { componentDef, componentKey, config, disabled, isSystemLocked } = selectedNode.data as {

Check warning on line 223 in src/components/flow/detail-panel.tsx

View workflow job for this annotation

GitHub Actions / Lint & Type Check

'componentKey' is assigned a value but never used
componentDef: VectorComponentDef;
componentKey: string;
config: Record<string, unknown>;
Expand Down Expand Up @@ -262,10 +278,13 @@
<Label htmlFor="component-key">Component Key</Label>
<Input
id="component-key"
value={componentKey}
value={displayKey}
onChange={(e) => handleKeyChange(e.target.value)}
disabled={isSystemLocked}
/>
<p className="text-xs text-muted-foreground">
Letters, numbers, and underscores only (e.g. traefik_logs)
</p>
</div>

{/* Enabled toggle */}
Expand Down
Loading