2026-03-22 12:20:54 -05:00
|
|
|
import { memo, useMemo } from 'react';
|
2026-03-21 21:48:56 -05:00
|
|
|
import { Handle, Position, type NodeProps } from '@xyflow/react';
|
|
|
|
|
import { Shield } from 'lucide-react';
|
|
|
|
|
|
|
|
|
|
export const FirewallNode = memo(({ data, selected }: NodeProps) => {
|
|
|
|
|
const label = (data as { label?: string }).label ?? 'Firewall';
|
2026-03-22 12:20:54 -05:00
|
|
|
|
|
|
|
|
const meta = useMemo(() => {
|
|
|
|
|
try {
|
|
|
|
|
return (data as any).metadata ? JSON.parse((data as any).metadata) : {};
|
|
|
|
|
} catch {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}, [data]);
|
|
|
|
|
|
|
|
|
|
const hasAddress = meta.ipAddress || meta.port;
|
2026-03-21 21:48:56 -05:00
|
|
|
return (
|
|
|
|
|
<div className={`min-w-[140px] bg-slate-800 border rounded-lg shadow-lg overflow-hidden ${selected ? 'ring-2 ring-red-500 border-red-500' : 'border-red-700'}`}>
|
|
|
|
|
<Handle type="target" position={Position.Top} className="!bg-red-400 !border-red-600" />
|
2026-03-22 12:20:54 -05:00
|
|
|
<div className="px-3 py-2 flex flex-col gap-1">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<Shield size={13} className="text-red-400 shrink-0" />
|
|
|
|
|
<span className="text-xs font-semibold text-slate-100 truncate">{label}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{hasAddress && (
|
|
|
|
|
<div className="text-[10px] text-slate-400 font-mono pl-[21px] truncate">
|
|
|
|
|
{meta.ipAddress}
|
|
|
|
|
{meta.ipAddress && meta.port && ':'}
|
|
|
|
|
{meta.port}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-03-21 21:48:56 -05:00
|
|
|
</div>
|
|
|
|
|
<Handle type="source" position={Position.Bottom} className="!bg-red-400 !border-red-600" />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
FirewallNode.displayName = 'FirewallNode';
|