feat: fully functioning ALB

This commit is contained in:
Zakaria
2026-09-26 16:14:24 -04:00
parent 093afb9b2b
commit 8470b8d17a
44 changed files with 2076 additions and 132 deletions
+20 -1
View File
@@ -12,6 +12,25 @@ const computeFilters: FieldSchema[] = [
{name: 'search', label: 'Search', type: 'text', required: false},
]
const awsComputeFilters: FieldSchema[] = [
...computeFilters,
{
name: 'state',
label: 'Lifecycle state',
type: 'select',
required: false,
options: [
{label: 'All states', value: ''},
{label: 'Running', value: 'running'},
{label: 'Stopped', value: 'stopped'},
{label: 'Pending', value: 'pending'},
{label: 'Stopping', value: 'stopping'},
{label: 'Shutting down', value: 'shutting-down'},
{label: 'Terminated', value: 'terminated'},
],
},
]
const computeFields: FieldSchema[] = [
// ── Required ──────────────────────────────────────────────────────
{
@@ -234,7 +253,7 @@ export function awsComputeSchema(): ServiceSchema {
displayName: 'Compute',
fields: computeFields,
actions: ['list', 'inspect', 'create', 'delete'],
filters: computeFilters,
filters: awsComputeFilters,
columns: computeColumns,
capabilities: {
resourceActions: [
+50
View File
@@ -0,0 +1,50 @@
import type {ItemActionName} from './childCollections'
import type {CapabilitySchema, FieldSchema, ResourceActionName, ServiceSchema, TableColumnSchema} from './types'
const resourceActions: CapabilitySchema<ResourceActionName>[] = [
{name: 'list', label: 'List repositories', enabled: true, status: 'available', runtimeRequired: true},
{name: 'create', label: 'Create repository', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Delete repository', enabled: true, status: 'available', runtimeRequired: true},
{name: 'inspect', label: 'Inspect repository', enabled: true, status: 'available', runtimeRequired: false},
]
const itemActions: CapabilitySchema<ItemActionName>[] = [
{name: 'list', label: 'List images', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Delete image', enabled: true, status: 'available', runtimeRequired: true},
]
const fields: FieldSchema[] = [
{
name: 'repositoryName',
label: 'Repository name',
type: 'text',
required: true,
description: 'Use lowercase letters, numbers, and separators. Names may include namespaces separated by slashes.',
validation: {
pattern: '^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$',
minLength: 2,
maxLength: 256,
message: 'Use a valid ECR repository name.',
},
},
]
const columns: TableColumnSchema[] = [
{name: 'name', label: 'Name'},
{name: 'uri', label: 'URI', path: 'metadata.repositoryUri', format: 'code'},
{name: 'tagMutability', label: 'Tag mutability', path: 'metadata.imageTagMutability', format: 'badge'},
{name: 'createdAt', label: 'Created at', format: 'datetime'},
]
export function awsEcrSchema(): ServiceSchema {
return {
cloud: 'aws',
service: 'ecr',
displayName: 'ECR',
fields,
actions: ['list', 'create', 'delete', 'inspect'],
capabilities: {resourceActions, itemActions},
filters: [{name: 'search', label: 'Search', type: 'text', required: false}],
columns,
}
}
@@ -11,7 +11,6 @@ const loadBalancerColumns: TableColumnSchema[] = [
const loadBalancerFilters: FieldSchema[] = [{name: 'search', label: 'Search', type: 'text', required: false}]
export const ELB_TYPES = ['application', 'network'] as const
export const ELB_SCHEMES = ['internet-facing', 'internal'] as const
/** ELBv2 names: up to 32 chars, alphanumerics and hyphens, no leading/trailing hyphen. */
@@ -48,14 +47,6 @@ export function awsLoadBalancingSchema(): ServiceSchema {
description:
'Two or more subnet ids, comma separated — e.g. subnet-aaa, subnet-bbb. ELB requires them to be in different availability zones.',
},
{
name: 'type',
label: 'Type',
type: 'select',
required: false,
description: 'Defaults to application.',
options: ELB_TYPES.map((value) => ({label: value, value})),
},
{
name: 'scheme',
label: 'Scheme',
@@ -65,7 +56,10 @@ export function awsLoadBalancingSchema(): ServiceSchema {
options: ELB_SCHEMES.map((value) => ({label: value, value})),
},
],
actions: ['list', 'create', 'delete', 'inspect'],
actions: ['list', 'create', 'update', 'delete', 'inspect'],
updateFields: [
{name: 'securityGroups', label: 'Security Groups', type: 'text', required: true, valuePath: 'metadata.securityGroups'},
],
capabilities: {
resourceActions: [
{name: 'list', label: 'List load balancers', enabled: true, status: 'available', runtimeRequired: true},
@@ -77,8 +71,14 @@ export function awsLoadBalancingSchema(): ServiceSchema {
status: 'available',
runtimeRequired: true,
},
{name: 'update', label: 'Set security groups', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Delete', enabled: true, status: 'available', runtimeRequired: true},
],
itemActions: [
{name: 'list', label: 'List listeners', enabled: true, status: 'available', runtimeRequired: true},
{name: 'put', label: 'Create listener', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Delete listener', enabled: true, status: 'available', runtimeRequired: true},
],
},
filters: loadBalancerFilters,
columns: loadBalancerColumns,
@@ -78,6 +78,13 @@ export const SERVICE_CATALOG = {
order: 40,
},
storage: {displayName: 'Storage', iconKey: 'storage', group: 'Storage', order: 10},
ecr: {
displayName: 'Elastic Container Registry',
displayNameByCloud: {aws: 'ECR'},
iconKey: 'ecr',
group: 'Storage',
order: 20,
},
database: {displayName: 'Database', iconKey: 'database', group: 'Databases', order: 10},
nosql: {
displayName: 'NoSQL',
@@ -104,6 +111,13 @@ export const SERVICE_CATALOG = {
group: 'Networking',
order: 20,
},
targetgroups: {
displayName: 'Target Groups',
displayNameByCloud: {aws: 'ELB Target Groups'},
iconKey: 'loadbalancing',
group: 'Networking',
order: 30,
},
messaging: {
displayName: 'Messaging',
displayNameByCloud: {aws: 'SQS', gcp: 'Pub/Sub'},
@@ -0,0 +1,92 @@
import type {ItemActionName} from './childCollections'
import type {CapabilitySchema, FieldSchema, ServiceSchema, TableColumnSchema} from './types'
import {ELB_NAME_PATTERN} from './loadBalancingSchema'
const targetGroupColumns: TableColumnSchema[] = [
{name: 'name', label: 'Name'},
{name: 'protocol', label: 'Protocol', path: 'metadata.protocol', format: 'badge'},
{name: 'port', label: 'Port', path: 'metadata.port'},
{name: 'targetType', label: 'Target Type', path: 'metadata.targetType'},
{name: 'vpcId', label: 'VPC', path: 'metadata.vpcId', format: 'code', emptyText: '—'},
{name: 'loadBalancerArns', label: 'Attached LBs', path: 'metadata.loadBalancerArns', format: 'list', emptyText: '—'},
]
const targetGroupFilters: FieldSchema[] = [{name: 'search', label: 'Search', type: 'text', required: false}]
const targetHealthActions: CapabilitySchema<ItemActionName>[] = [
{name: 'list', label: 'Describe target health', enabled: true, status: 'available', runtimeRequired: true},
{name: 'put', label: 'Register target', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Deregister target', enabled: true, status: 'available', runtimeRequired: true},
]
const TARGET_GROUP_PROTOCOLS = ['HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP', 'GENEVE']
const TARGET_TYPES = ['instance', 'ip', 'lambda', 'alb']
const HEALTH_CHECK_PROTOCOLS = ['HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP', 'GENEVE']
export function awsTargetGroupsSchema(): ServiceSchema {
return {
cloud: 'aws',
service: 'targetgroups',
displayName: 'AWS ELB Target Groups',
fields: [
{
name: 'name',
label: 'Target Group Name',
type: 'text',
required: true,
group: 'Required',
validation: {
pattern: ELB_NAME_PATTERN,
minLength: 1,
maxLength: 32,
message: 'Up to 32 characters: letters, digits and hyphens, not starting or ending with a hyphen.',
},
},
{
name: 'protocol',
label: 'Protocol',
type: 'select',
required: true,
group: 'Required',
options: TARGET_GROUP_PROTOCOLS.map((value) => ({label: value, value})),
},
{
name: 'port',
label: 'Port',
type: 'text',
required: true,
group: 'Required',
validation: {pattern: '^[1-9][0-9]{0,4}$', message: 'Enter a port from 1 to 65535.'},
},
{
name: 'targetType',
label: 'Target Type',
type: 'select',
required: false,
description: 'Defaults to instance.',
options: TARGET_TYPES.map((value) => ({label: value, value})),
},
{name: 'vpcId', label: 'VPC ID', type: 'text', required: false},
{
name: 'healthCheckProtocol',
label: 'Health Check Protocol',
type: 'select',
required: false,
options: HEALTH_CHECK_PROTOCOLS.map((value) => ({label: value, value})),
},
{name: 'healthCheckPath', label: 'Health Check Path', type: 'text', required: false},
],
actions: ['list', 'create', 'delete', 'inspect'],
capabilities: {
resourceActions: [
{name: 'list', label: 'List target groups', enabled: true, status: 'available', runtimeRequired: true},
{name: 'inspect', label: 'Inspect', enabled: true, status: 'available', runtimeRequired: true},
{name: 'create', label: 'Create target group', enabled: true, status: 'available', runtimeRequired: true},
{name: 'delete', label: 'Delete', enabled: true, status: 'available', runtimeRequired: true},
],
itemActions: targetHealthActions,
},
filters: targetGroupFilters,
columns: targetGroupColumns,
}
}
+3
View File
@@ -433,6 +433,7 @@ export interface AppConfigDeployment {
export interface ResourceQuery {
search?: string
state?: string
}
export interface CreateResourceInput {
@@ -643,6 +644,8 @@ export interface CloudServiceAdapter {
get(id: string): Promise<CloudResource | null>
create(input: CreateResourceInput): Promise<CloudResource>
update?(id: string, input: UpdateResourceInput): Promise<CloudResource>
/** Service-specific provider data used to populate resource forms. */
dependencies?(): Promise<Record<string, unknown>>
delete(id: string): Promise<void>
listObjects?(resourceId: string, prefix?: string): Promise<StorageObjectList>
putObject?(resourceId: string, key: string, body: Uint8Array, contentType: string): Promise<void>