feat: fully functioning ALB
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
AlertTriangle,
|
||||
@@ -583,6 +583,22 @@ function InstancePanel({ instanceId }: { instanceId: string }) {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{inst.networkInterfaces.length > 0 && (
|
||||
<div className="meta-row" style={{ gridColumn: '1 / -1', alignItems: 'start' }}>
|
||||
<span className="meta-label">Network interfaces</span>
|
||||
<span className="meta-value" style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
{inst.networkInterfaces.map((networkInterface) => (
|
||||
<span key={networkInterface.networkInterfaceId} style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
||||
<code>{networkInterface.networkInterfaceId}</code>
|
||||
{networkInterface.deviceIndex !== undefined && <span>device {networkInterface.deviceIndex}</span>}
|
||||
{networkInterface.status && <span>{networkInterface.status}</span>}
|
||||
{networkInterface.privateIpAddress && <span>private {networkInterface.privateIpAddress}</span>}
|
||||
{networkInterface.publicIpAddress && <span>public {networkInterface.publicIpAddress}</span>}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -789,6 +805,23 @@ export function LaunchInstanceForm({
|
||||
const vpcsQ = useQuery({ queryKey: ['ec2', 'vpcs'], queryFn: ({ signal }) => listEc2Vpcs(signal) })
|
||||
const subsQ = useQuery({ queryKey: ['ec2', 'subnets'], queryFn: ({ signal }) => listEc2Subnets(undefined, signal) })
|
||||
const sgsQ = useQuery({ queryKey: ['ec2', 'security-groups'], queryFn: ({ signal }) => listEc2SecurityGroups(undefined, signal) })
|
||||
const selectedSubnet = (subsQ.data ?? []).find((subnet) => subnet.subnetId === subnetId)
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedSubnet) {
|
||||
return
|
||||
}
|
||||
const compatibleGroupIds = new Set((sgsQ.data ?? [])
|
||||
.filter((group) => group.vpcId === selectedSubnet.vpcId)
|
||||
.map((group) => group.groupId))
|
||||
setSelectedSgs((current) => {
|
||||
const compatible = current.filter((groupId) => compatibleGroupIds.has(groupId))
|
||||
if (compatible.length !== current.length) {
|
||||
setStepError('Security groups from another VPC were removed. Select groups in the subnet VPC.')
|
||||
}
|
||||
return compatible
|
||||
})
|
||||
}, [selectedSubnet, sgsQ.data])
|
||||
|
||||
const createMut = useMutation({
|
||||
mutationFn: (values: Record<string, unknown>) => createCloudResource(cloud, 'compute', values),
|
||||
@@ -845,6 +878,13 @@ export function LaunchInstanceForm({
|
||||
}
|
||||
|
||||
function launch() {
|
||||
const hasIncompatibleSecurityGroup = selectedSubnet && selectedSgs.some((groupId) =>
|
||||
!(sgsQ.data ?? []).some((group) => group.groupId === groupId && group.vpcId === selectedSubnet.vpcId))
|
||||
if (hasIncompatibleSecurityGroup) {
|
||||
setStep(3)
|
||||
setStepError('Security groups must belong to the selected subnet VPC.')
|
||||
return
|
||||
}
|
||||
createMut.mutate({
|
||||
name,
|
||||
imageId,
|
||||
@@ -908,8 +948,8 @@ export function LaunchInstanceForm({
|
||||
return !quickStartImageIds.has(ami.imageId) && (!normalizedAmiSearch || searchable.includes(normalizedAmiSearch))
|
||||
})
|
||||
const selectedAmi = amis.find((ami) => ami.imageId === imageId)
|
||||
const selectedSubnet = subnets.find((subnet) => subnet.subnetId === subnetId)
|
||||
const selectedGroups = (sgsQ.data ?? []).filter((group) => selectedSgs.includes(group.groupId))
|
||||
const availableSecurityGroups = (sgsQ.data ?? []).filter((group) => !selectedSubnet || group.vpcId === selectedSubnet.vpcId)
|
||||
const steps: Array<{ number: LaunchStep; label: string }> = [
|
||||
{ number: 1, label: 'Name and AMI' },
|
||||
{ number: 2, label: 'Instance and key pair' },
|
||||
@@ -1046,10 +1086,11 @@ export function LaunchInstanceForm({
|
||||
|
||||
<div className="sg-checklist">
|
||||
{sgsQ.isLoading && <span className="muted">Loading…</span>}
|
||||
{selectedSubnet && <span className="muted">Showing groups in {selectedSubnet.vpcId} only.</span>}
|
||||
{sgsQ.data?.length === 0 && !showCreateSg && (
|
||||
<span className="muted">No security groups — create one above.</span>
|
||||
)}
|
||||
{(sgsQ.data ?? []).map((sg: Ec2SecurityGroup) => (
|
||||
{availableSecurityGroups.map((sg: Ec2SecurityGroup) => (
|
||||
<label key={sg.groupId} className="sg-check-item">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
Reference in New Issue
Block a user