|
|
x |
|
|
const { useState, useEffect } = React; |
|
|
|
|
|
const FlowNode = ({ id, title, details, type, position, onClick }) => { |
|
|
const getNodeClass = (type) => { |
|
|
switch (type) { |
|
|
case 'internet': |
|
|
return 'internet-node'; |
|
|
case 'igw': |
|
|
return 'igw-node'; |
|
|
case 'subnet': |
|
|
return 'subnet-node'; |
|
|
case 'ec2': |
|
|
return 'ec2-node'; |
|
|
case 'lb': |
|
|
return 'lb-node'; |
|
|
default: |
|
|
return 'default-node'; |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div |
|
|
className={`flow-node ${getNodeClass(type)}`} |
|
|
style={{ left: position.x, top: position.y }} |
|
|
onClick={() => onClick && onClick({ id, title, details, type })} |
|
|
> |
|
|
<div className="node-title">{title}</div> |
|
|
{details && <div className="node-details">{details}</div>} |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
const FlowArrow = ({ from, to }) => { |
|
|
const deltaX = to.x - from.x; |
|
|
const deltaY = to.y - from.y; |
|
|
const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY); |
|
|
const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI; |
|
|
|
|
|
return ( |
|
|
<div |
|
|
className="flow-arrow" |
|
|
style={{ |
|
|
left: from.x, |
|
|
top: from.y, |
|
|
width: length, |
|
|
transform: `rotate(${angle}deg)`, |
|
|
transformOrigin: '0 0' |
|
|
}} |
|
|
/> |
|
|
); |
|
|
}; |
|
|
|
|
|
const VPCDiagram = () => { |
|
|
const [selectedComponent, setSelectedComponent] = useState(null); |
|
|
|
|
|
|
|
|
const prodFlow = { |
|
|
internet: { id: 'prod-internet', title: 'Internet', type: 'internet', position: { x: 50, y: 100 } }, |
|
|
igw: { id: 'prod-igw', title: 'Internet Gateway', details: 'igw-09cf4bcb7239a6547', type: 'igw', position: { x: 300, y: 100 } }, |
|
|
pubsub1: { id: 'prod-pubsub1', title: 'Public Subnet', details: '15.0.0.0/20\nus-east-1a', type: 'subnet', position: { x: 550, y: 50 } }, |
|
|
pubsub2: { id: 'prod-pubsub2', title: 'Public Subnet', details: '15.0.16.0/20\nus-east-1b', type: 'subnet', position: { x: 550, y: 150 } }, |
|
|
ec2prod1: { id: 'prod-ec2-1', title: 'BE Prod', details: '3.220.214.17\nt2.small', type: 'ec2', position: { x: 800, y: 20 } }, |
|
|
ec2prod2: { id: 'prod-ec2-2', title: 'BE Admin Prod', details: '3.227.125.175\nt2.small', type: 'ec2', position: { x: 800, y: 80 } }, |
|
|
ec2ml: { id: 'prod-ec2-ml', title: 'ML Prod', details: '98.83.117.30\nt2.medium', type: 'ec2', position: { x: 800, y: 140 } }, |
|
|
lbprod: { id: 'prod-lb', title: 'Internal Load Balancer', details: 'ieq-prod-lb', type: 'lb', position: { x: 1050, y: 80 } } |
|
|
}; |
|
|
|
|
|
|
|
|
const defaultFlow = { |
|
|
internet: { id: 'def-internet', title: 'Internet', type: 'internet', position: { x: 50, y: 400 } }, |
|
|
igw: { id: 'def-igw', title: 'Internet Gateway', details: 'igw-08f0068d5deb99f34', type: 'igw', position: { x: 300, y: 400 } }, |
|
|
|
|
|
|
|
|
devbe: { id: 'dev-be', title: 'BE Dev', details: '23.22.6.48\nt2.small', type: 'ec2', position: { x: 600, y: 320 } }, |
|
|
devadmin: { id: 'dev-admin', title: 'BE Admin Dev', details: '34.225.40.84\nt2.micro', type: 'ec2', position: { x: 600, y: 370 } }, |
|
|
devml: { id: 'dev-ml', title: 'ML Dev', details: '54.225.50.5\nt2.medium', type: 'ec2', position: { x: 600, y: 420 } }, |
|
|
devav: { id: 'dev-av', title: 'AV Processing', details: '54.224.121.98\nt2.small', type: 'ec2', position: { x: 600, y: 470 } }, |
|
|
|
|
|
|
|
|
prebe: { id: 'pre-be', title: 'BE PreProd', details: '44.214.177.246\nt2.small', type: 'ec2', position: { x: 900, y: 320 } }, |
|
|
preadmin: { id: 'pre-admin', title: 'BE Admin PreProd', details: '34.193.219.91\nt2.small', type: 'ec2', position: { x: 900, y: 370 } }, |
|
|
preml: { id: 'pre-ml', title: 'ML PreProd', details: '18.234.27.117\nc5.2xlarge', type: 'ec2', position: { x: 900, y: 420 } }, |
|
|
premcp: { id: 'pre-mcp', title: 'MCP PreProd', details: '44.204.54.3\nt2.medium', type: 'ec2', position: { x: 900, y: 470 } }, |
|
|
|
|
|
|
|
|
lbdev: { id: 'dev-lb', title: 'ALB Dev', details: 'ieq-be-dev\nieq-be-admin-dev-lb', type: 'lb', position: { x: 1200, y: 345 } }, |
|
|
lbmcp: { id: 'pre-lb', title: 'Internal ALB', details: 'ieq-mcp-stage-lb', type: 'lb', position: { x: 1200, y: 445 } } |
|
|
}; |
|
|
|
|
|
|
|
|
const prodConnections = [ |
|
|
{ from: 'prod-internet', to: 'prod-igw' }, |
|
|
{ from: 'prod-igw', to: 'prod-pubsub1' }, |
|
|
{ from: 'prod-igw', to: 'prod-pubsub2' }, |
|
|
{ from: 'prod-pubsub1', to: 'prod-ec2-1' }, |
|
|
{ from: 'prod-pubsub1', to: 'prod-ec2-2' }, |
|
|
{ from: 'prod-pubsub1', to: 'prod-ec2-ml' }, |
|
|
{ from: 'prod-lb', to: 'prod-ec2-1' } |
|
|
]; |
|
|
|
|
|
|
|
|
const defaultConnections = [ |
|
|
{ from: 'def-internet', to: 'def-igw' }, |
|
|
{ from: 'def-igw', to: 'dev-be' }, |
|
|
{ from: 'def-igw', to: 'dev-admin' }, |
|
|
{ from: 'def-igw', to: 'dev-ml' }, |
|
|
{ from: 'def-igw', to: 'dev-av' }, |
|
|
{ from: 'def-igw', to: 'pre-be' }, |
|
|
{ from: 'def-igw', to: 'pre-admin' }, |
|
|
{ from: 'def-igw', to: 'pre-ml' }, |
|
|
{ from: 'def-igw', to: 'pre-mcp' }, |
|
|
{ from: 'dev-lb', to: 'dev-be' }, |
|
|
{ from: 'dev-lb', to: 'dev-admin' }, |
|
|
{ from: 'pre-lb', to: 'pre-mcp' } |
|
|
]; |
|
|
|
|
|
const VPCSection = ({ title, vpc, flow, connections }) => ( |
|
|
<div className="vpc-flow-section"> |
|
|
<div className="vpc-flow-header"> |
|
|
<h2>{title}</h2> |
|
|
</div> |
|
|
<div className="flow-diagram"> |
|
|
{connections.map((conn, index) => ( |
|
|
<FlowArrow |
|
|
key={index} |
|
|
from={flow[conn.from].position} |
|
|
to={flow[conn.to].position} |
|
|
/> |
|
|
))} |
|
|
{Object.values(flow).map((node) => ( |
|
|
<FlowNode |
|
|
key={node.id} |
|
|
id={node.id} |
|
|
title={node.title} |
|
|
details={node.details} |
|
|
type={node.type} |
|
|
position={node.position} |
|
|
onClick={setSelectedComponent} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
|
|
|
const ComponentDetails = () => { |
|
|
if (!selectedComponent) return null; |
|
|
|
|
|
return ( |
|
|
<div className="component-details-overlay" onClick={() => setSelectedComponent(null)}> |
|
|
<div className="component-details-panel" onClick={(e) => e.stopPropagation()}> |
|
|
<h3>{selectedComponent.title}</h3> |
|
|
{selectedComponent.details && <p>{selectedComponent.details}</p>} |
|
|
<p>Type: {selectedComponent.type}</p> |
|
|
<button onClick={() => setSelectedComponent(null)}>Close</button> |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
useEffect(() => { |
|
|
feather.replace(); |
|
|
}, []); |
|
|
|
|
|
return ( |
|
|
<div className="container"> |
|
|
<div className="header"> |
|
|
<h1>AWS VPC Traffic Flow Diagram</h1> |
|
|
<p>Interactive visualization of Production and Development VPC traffic patterns</p> |
|
|
</div> |
|
|
|
|
|
<div className="diagram-container"> |
|
|
<VPCSection |
|
|
title="Production VPC - 15.0.0.0/16" |
|
|
vpc="production" |
|
|
flow={prodFlow} |
|
|
connections={prodConnections} |
|
|
/> |
|
|
|
|
|
<VPCSection |
|
|
title="Default VPC - 172.31.0.0/16 (Dev & PreProd)" |
|
|
vpc="default" |
|
|
flow={defaultFlow} |
|
|
connections={defaultConnections} |
|
|
/> |
|
|
</div> |
|
|
|
|
|
<ComponentDetails /> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
const root = ReactDOM.createRoot(document.getElementById('root')); |
|
|
root.render(<VPCDiagram />); |
|
|
|