import {Hono} from 'hono' import type {Context} from 'hono' import type { CloudProvider, CloudServiceType, CreateDatabaseSnapshotInput, KmsEncryptionAlgorithm, S3BucketWebsiteConfiguration, SqlConnectionInput, } from '../cloud-spi/types' import {clampLimit, type PageQuery} from '../cloud-spi/childCollections' import {CloudError, toHttpError, ValidationError} from '../cloud-spi/errors' import {isServiceType} from '../cloud-spi/serviceCatalog' import {mapAwsSdkError} from '../adapter-aws/awsErrors' import {serviceForAccount} from '../cloudProxy' import {CloudProxyService} from '../service/CloudProxyService' import {runtimeDiagnostics} from '../service/runtimeDiagnostics' import {deployLightsailNodeApplication, deployLightsailStaticFiles} from '../services/lightsailDeployment' // Header (and query-param fallback for direct links such as object downloads) // used by the frontend to scope every request to an AWS account. export const ACCOUNT_HEADER = 'x-floci-account-id' interface KmsEncryptRequest { plaintextBase64?: unknown encryptionAlgorithm?: unknown encryptionContext?: unknown } interface KmsDecryptRequest { ciphertextBlobBase64?: unknown encryptionAlgorithm?: unknown encryptionContext?: unknown } const KMS_ENCRYPTION_ALGORITHMS = new Set([ 'SYMMETRIC_DEFAULT', 'RSAES_OAEP_SHA_1', 'RSAES_OAEP_SHA_256', ]) const MAX_PLAINTEXT_BASE64_LENGTH = 5_464 const MAX_CIPHERTEXT_BASE64_LENGTH = 8_192 export function createCloudRoutes(injectedService?: CloudProxyService) { const app = new Hono() // Resolve the account-scoped service per request. An explicitly injected // service (used by tests) always wins and ignores the account header. const svc = (c: Context): CloudProxyService => injectedService ?? serviceForAccount(c.req.header(ACCOUNT_HEADER) ?? c.req.query('account')) app.get('/', (c) => c.json(svc(c).clouds())) app.get('/:cloud/services', (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return c.json(svc(c).services(cloud)) }) app.get('/:cloud/status', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) // Per-service detail is opt-in; the connection indicator polls this often. const includeServices = c.req.query('services') === 'all' return c.json(await svc(c).status(cloud, {includeServices})) }) app.get('/:cloud/diagnostics', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return c.json(await runtimeDiagnostics(cloud, svc(c))) }) app.get('/:cloud/services/:service/status', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return c.json(await svc(c).serviceStatus(cloud, serviceType)) }) app.get('/:cloud/services/:service/schema', (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) const schema = svc(c).schema(cloud, serviceType) if (!schema) { return c.json({error: 'Schema not available'}, 404) } return c.json(schema) }) app.get('/:cloud/services/:service/dependencies', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).dependencies(cloud, serviceType))) }) app.get('/:cloud/services/database/snapshots', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const snapshots = await svc(c).listDatabaseSnapshots(cloud, c.req.query('instanceIdentifier')) return c.json(snapshots) }) }) app.post('/:cloud/services/database/snapshots', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const input = await c.req.json() const snapshot = await svc(c).createDatabaseSnapshot(cloud, input) return c.json(snapshot, 201) }) }) app.get('/:cloud/services/database/orderable-classes', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const classes = await svc(c).listDatabaseOrderableInstanceClasses(cloud, c.req.query('engine')) return c.json(classes) }) }) app.get('/:cloud/services/:service/resources', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const resources = await svc(c).listResources(cloud, serviceType, { search: c.req.query('search'), state: c.req.query('state'), }) return c.json(resources) }) }) app.post('/aws/services/lightsail/resources/:id/static-files', async (c) => { return withRuntime(c, async () => { const form = await c.req.formData() const files = form.getAll('files').filter((entry): entry is File => entry instanceof File) if (files.length === 0 || files.length !== form.getAll('files').length) { throw new ValidationError('At least one file is required.') } const upload = new FormData() for (const file of files) { upload.append('files', file, file.name) } return c.json(await deployLightsailStaticFiles(c.req.param('id'), upload)) }) }) app.post('/aws/services/lightsail/resources/:id/node-application', async (c) => { return withRuntime(c, async () => { const form = await c.req.formData() const files = form.getAll('files').filter((entry): entry is File => entry instanceof File) if (files.length === 0 || files.length !== form.getAll('files').length) { throw new ValidationError('At least one file or zip archive is required.') } const portValue = form.get('port') const port = typeof portValue === 'string' && /^\d+$/.test(portValue) ? Number(portValue) : 3000 if (port < 1 || port > 65535) throw new ValidationError('Port must be between 1 and 65535.') const upload = new FormData() for (const file of files) upload.append('files', file, file.name) const startCommand = form.get('startCommand') if (typeof startCommand === 'string' && startCommand.trim()) upload.append('startCommand', startCommand.trim()) upload.append('port', String(port)) return c.json(await deployLightsailNodeApplication(c.req.param('id'), upload)) }) }) app.get('/:cloud/services/nosql/resources/:id/containers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const containers = await svc(c).listCosmosContainers(cloud, c.req.param('id')) return c.json(containers) }) }) app.post('/:cloud/services/nosql/resources/:id/containers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() const container = await svc(c).createCosmosContainer(cloud, c.req.param('id'), {values}) return c.json(container, 201) }) }) app.delete('/:cloud/services/nosql/resources/:id/containers/:containerId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteCosmosContainer(cloud, c.req.param('id'), c.req.param('containerId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/nosql/resources/:id/containers/:containerId/items', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const items = await svc(c).listCosmosItems(cloud, c.req.param('id'), c.req.param('containerId')) return c.json(items) }) }) app.post('/:cloud/services/nosql/resources/:id/containers/:containerId/items', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const document = await c.req.json>() const item = await svc(c).upsertCosmosItem(cloud, c.req.param('id'), c.req.param('containerId'), document) return c.json(item, 201) }) }) app.delete('/:cloud/services/nosql/resources/:id/containers/:containerId/items/:itemId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteCosmosItem(cloud, c.req.param('id'), c.req.param('containerId'), c.req.param('itemId'), c.req.query('partitionKey') ?? null) return c.json({ok: true}) }) }) app.post('/:cloud/services/nosql/resources/:id/containers/:containerId/query', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{query?: string}>() const result = await svc(c).queryCosmosItems(cloud, c.req.param('id'), c.req.param('containerId'), body.query ?? '') return c.json(result) }) }) app.post('/:cloud/services/database/resources/:id/sql/databases', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const connection = await c.req.json() const databases = await svc(c).listSqlDatabases(cloud, c.req.param('id'), connection) return c.json(databases) }) }) app.post('/:cloud/services/database/resources/:id/sql/tables', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const connection = await c.req.json() const tables = await svc(c).listSqlTables(cloud, c.req.param('id'), connection) return c.json(tables) }) }) app.post('/:cloud/services/database/resources/:id/sql/query', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json() const {query = '', ...connection} = body const result = await svc(c).querySql(cloud, c.req.param('id'), connection, query) return c.json(result) }) }) app.post('/:cloud/services/logs/resources/:id/query', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{queryString?: unknown; startTime?: unknown; endTime?: unknown; limit?: unknown}>() if (typeof body.queryString !== 'string') throw new ValidationError('queryString must be a string.') if (typeof body.startTime !== 'number') throw new ValidationError('startTime must be a number (epoch seconds).') if (typeof body.endTime !== 'number') throw new ValidationError('endTime must be a number (epoch seconds).') if (body.limit !== undefined && typeof body.limit !== 'number') throw new ValidationError('limit must be a number.') const result = await svc(c).queryLogs(cloud, c.req.param('id'), { queryString: body.queryString, startTime: body.startTime, endTime: body.endTime, limit: body.limit, }) return c.json(result) }) }) app.post('/:cloud/services/logs/resources/:id/filter', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{filterPattern?: unknown; logStreamNames?: unknown; startTime?: unknown; endTime?: unknown}>() if (body.filterPattern !== undefined && typeof body.filterPattern !== 'string') throw new ValidationError('filterPattern must be a string.') if (body.logStreamNames !== undefined && (!Array.isArray(body.logStreamNames) || body.logStreamNames.some((name) => typeof name !== 'string'))) throw new ValidationError('logStreamNames must be an array of strings.') if (body.startTime !== undefined && typeof body.startTime !== 'number') throw new ValidationError('startTime must be a number (epoch milliseconds).') if (body.endTime !== undefined && typeof body.endTime !== 'number') throw new ValidationError('endTime must be a number (epoch milliseconds).') if (typeof body.startTime === 'number' && typeof body.endTime === 'number' && body.startTime > body.endTime) throw new ValidationError('startTime must not be after endTime.') return c.json(await svc(c).filterLogEvents(cloud, c.req.param('id'), { filterPattern: body.filterPattern, logStreamNames: body.logStreamNames as string[] | undefined, startTime: body.startTime, endTime: body.endTime, }, pageQuery(c))) }) }) app.post('/:cloud/services/messaging/resources/:id/messages', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{messageBody?: unknown; messageAttributes?: unknown; messageGroupId?: unknown; messageDeduplicationId?: unknown}>() if (typeof body.messageBody !== 'string') throw new ValidationError('messageBody must be a string.') return c.json(await svc(c).sendSqsMessage(cloud, c.req.param('id'), { messageBody: body.messageBody, messageAttributes: body.messageAttributes as never, messageGroupId: typeof body.messageGroupId === 'string' ? body.messageGroupId : undefined, messageDeduplicationId: typeof body.messageDeduplicationId === 'string' ? body.messageDeduplicationId : undefined, }), 201) }) }) app.post('/:cloud/services/messaging/resources/:id/messages/receive', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{maxNumberOfMessages?: unknown}>() if (body.maxNumberOfMessages !== undefined && typeof body.maxNumberOfMessages !== 'number') throw new ValidationError('maxNumberOfMessages must be a number.') return c.json(await svc(c).receiveSqsMessages(cloud, c.req.param('id'), {maxNumberOfMessages: body.maxNumberOfMessages})) }) }) app.post('/:cloud/services/messaging/resources/:id/messages/delete', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{receiptHandle?: unknown}>() if (typeof body.receiptHandle !== 'string' || !body.receiptHandle) throw new ValidationError('receiptHandle is required.') await svc(c).deleteSqsMessage(cloud, c.req.param('id'), body.receiptHandle) return c.json({ok: true}) }) }) app.delete('/:cloud/services/messaging/resources/:id/messages', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).purgeSqsQueue(cloud, c.req.param('id')) return c.json({ok: true}) }) }) app.post('/:cloud/services/messaging/resources/:id/move-tasks', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{destinationArn?: unknown}>() if (body.destinationArn !== undefined && typeof body.destinationArn !== 'string') throw new ValidationError('destinationArn must be a string.') return c.json(await svc(c).startSqsMoveTask(cloud, c.req.param('id'), {destinationArn: body.destinationArn})) }) }) app.delete('/:cloud/services/email/inbox', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).clearEmailInbox(cloud) return c.json({ok: true}) }) }) app.post('/:cloud/services/userpools/resources/:id/users/:username/actions', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).updateUserPoolUser(cloud, c.req.param('id'), c.req.param('username'), await c.req.json()) return c.json({ok: true}) }) }) app.put('/:cloud/services/userpools/resources/:id/users/:username/groups', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{groups?: string[]}>() await svc(c).setUserPoolUserGroups(cloud, c.req.param('id'), c.req.param('username'), body.groups as string[]) return c.json({ok: true}) }) }) app.get('/:cloud/services/userpools/resources/:id/groups', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => c.json(await svc(c).listUserPoolGroups(cloud, c.req.param('id')))) }) app.post('/:cloud/services/userpools/resources/:id/groups', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const group = await svc(c).createUserPoolGroup(cloud, c.req.param('id'), await c.req.json()) return c.json(group, 201) }) }) app.delete('/:cloud/services/userpools/resources/:id/groups/:groupName', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteUserPoolGroup(cloud, c.req.param('id'), c.req.param('groupName')) return c.json({ok: true}) }) }) app.post('/:cloud/services/userpools/resources/:id/sign-in-test', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => c.json(await svc(c).testUserPoolSignIn(cloud, c.req.param('id'), await c.req.json()))) }) app.get('/:cloud/services/userpools/resources/:id/clients/:clientId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => c.json(await svc(c).describeUserPoolClient(cloud, c.req.param('id'), c.req.param('clientId')))) }) app.put('/:cloud/services/userpools/resources/:id/clients/:clientId/oauth', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => c.json(await svc(c).updateUserPoolClientOAuth(cloud, c.req.param('id'), c.req.param('clientId'), await c.req.json()))) }) app.put('/:cloud/services/cdn/resources/:id/enabled', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{enabled?: boolean}>() return c.json(await svc(c).setDistributionEnabled(cloud, c.req.param('id'), body.enabled as boolean)) }) }) app.post('/:cloud/services/cdn/resources/:id/invalidations', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{paths?: string[]}>() return c.json(await svc(c).createDistributionInvalidation(cloud, c.req.param('id'), body.paths as string[]), 201) }) }) app.get('/:cloud/services/k8s/resources/:id/nodegroups', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const nodegroups = await svc(c).listKubernetesNodegroups(cloud, c.req.param('id')) return c.json(nodegroups) }) }) app.post('/:cloud/services/k8s/resources/:id/nodegroups', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const nodegroup = await svc(c).createKubernetesNodegroup(cloud, c.req.param('id'), await c.req.json()) return c.json(nodegroup, 201) }) }) app.delete('/:cloud/services/k8s/resources/:id/nodegroups/:nodegroupId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteKubernetesNodegroup(cloud, c.req.param('id'), c.req.param('nodegroupId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/k8s/resources/:id/fargate-profiles', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const profiles = await svc(c).listKubernetesFargateProfiles(cloud, c.req.param('id')) return c.json(profiles) }) }) app.post('/:cloud/services/k8s/resources/:id/fargate-profiles', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const profile = await svc(c).createKubernetesFargateProfile(cloud, c.req.param('id'), await c.req.json()) return c.json(profile, 201) }) }) app.delete('/:cloud/services/k8s/resources/:id/fargate-profiles/:profileId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteKubernetesFargateProfile(cloud, c.req.param('id'), c.req.param('profileId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/configuration/resources/:id/environments', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { return c.json(await svc(c).listAppConfigEnvironments(cloud, c.req.param('id'))) }) }) app.post('/:cloud/services/configuration/resources/:id/environments', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() return c.json(await svc(c).createAppConfigEnvironment(cloud, c.req.param('id'), {values}), 201) }) }) app.delete('/:cloud/services/configuration/resources/:id/environments/:environmentId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteAppConfigEnvironment(cloud, c.req.param('id'), c.req.param('environmentId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/configuration/resources/:id/configuration-profiles', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { return c.json(await svc(c).listAppConfigConfigurationProfiles(cloud, c.req.param('id'))) }) }) app.post('/:cloud/services/configuration/resources/:id/configuration-profiles', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() return c.json(await svc(c).createAppConfigConfigurationProfile(cloud, c.req.param('id'), {values}), 201) }) }) app.delete('/:cloud/services/configuration/resources/:id/configuration-profiles/:profileId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteAppConfigConfigurationProfile(cloud, c.req.param('id'), c.req.param('profileId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/configuration/resources/:id/configuration-profiles/:profileId/hosted-configuration-versions', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { return c.json(await svc(c).listAppConfigHostedConfigurationVersions(cloud, c.req.param('id'), c.req.param('profileId'))) }) }) app.post('/:cloud/services/configuration/resources/:id/configuration-profiles/:profileId/hosted-configuration-versions', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() return c.json(await svc(c).createAppConfigHostedConfigurationVersion(cloud, c.req.param('id'), c.req.param('profileId'), {values}), 201) }) }) const hostedVersionNumber = (raw: string): number | null => { const value = Number(raw) return Number.isInteger(value) && value > 0 ? value : null } app.get('/:cloud/services/configuration/resources/:id/configuration-profiles/:profileId/hosted-configuration-versions/:versionNumber', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const versionNumber = hostedVersionNumber(c.req.param('versionNumber')) if (!isCloudProvider(cloud) || versionNumber === null) return c.json({error: 'Unknown cloud or version'}, 404) return withRuntime(c, async () => { const version = await svc(c).getAppConfigHostedConfigurationVersion(cloud, c.req.param('id'), c.req.param('profileId'), versionNumber) if (!version) return c.json({error: 'Hosted configuration version not found'}, 404) return c.json(version) }) }) app.delete('/:cloud/services/configuration/resources/:id/configuration-profiles/:profileId/hosted-configuration-versions/:versionNumber', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const versionNumber = hostedVersionNumber(c.req.param('versionNumber')) if (!isCloudProvider(cloud) || versionNumber === null) return c.json({error: 'Unknown cloud or version'}, 404) return withRuntime(c, async () => { await svc(c).deleteAppConfigHostedConfigurationVersion(cloud, c.req.param('id'), c.req.param('profileId'), versionNumber) return c.json({ok: true}) }) }) app.get('/:cloud/services/configuration/deployment-strategies', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { return c.json(await svc(c).listAppConfigDeploymentStrategies(cloud)) }) }) app.post('/:cloud/services/configuration/deployment-strategies', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() return c.json(await svc(c).createAppConfigDeploymentStrategy(cloud, {values}), 201) }) }) app.delete('/:cloud/services/configuration/deployment-strategies/:strategyId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteAppConfigDeploymentStrategy(cloud, c.req.param('strategyId')) return c.json({ok: true}) }) }) app.post('/:cloud/services/configuration/resources/:id/environments/:environmentId/deployments', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() return c.json(await svc(c).startAppConfigDeployment(cloud, c.req.param('id'), c.req.param('environmentId'), {values}), 201) }) }) app.get('/:cloud/services/configuration/resources/:id/environments/:environmentId/deployments/:deploymentNumber', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const deploymentNumber = Number(c.req.param('deploymentNumber')) if (!isCloudProvider(cloud) || !Number.isInteger(deploymentNumber) || deploymentNumber <= 0) { return c.json({error: 'Unknown cloud or deployment'}, 404) } return withRuntime(c, async () => { const deployment = await svc(c).getAppConfigDeployment(cloud, c.req.param('id'), c.req.param('environmentId'), deploymentNumber) if (!deployment) return c.json({error: 'Deployment not found'}, 404) return c.json(deployment) }) }) // Child collections, parameterised by service. All the literal-segment // routes above (Cosmos containers, SQL, NoSQL items, email inbox, k8s // nodegroups/fargate profiles) must stay registered before these, or the // `:service` param below would swallow their requests first. app.post('/:cloud/services/apigateway/resources/:id/routes', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const route = await svc(c).createApiGatewayLambdaRoute(cloud, c.req.param('id'), { path: typeof input.path === 'string' ? input.path : '', parentResourceId: typeof input.parentResourceId === 'string' ? input.parentResourceId : undefined, pathPart: typeof input.pathPart === 'string' ? input.pathPart : undefined, method: typeof input.method === 'string' ? input.method : '', stage: typeof input.stage === 'string' ? input.stage : '', functionName: typeof input.functionName === 'string' ? input.functionName : '', }) return c.json(route, 201) }) }) app.get('/:cloud/services/apigateway/resources/:id/routes', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listApiGatewayRoutes(cloud, c.req.param('id')))) }) app.delete('/:cloud/services/apigateway/resources/:id/routes/:resourceId/:method', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteApiGatewayRoute(cloud, c.req.param('id'), { resourceId: c.req.param('resourceId'), path: c.req.query('path') ?? '', method: c.req.param('method'), stage: c.req.query('stage') ?? '', }) return c.json({ok: true}) }) }) app.post('/:cloud/services/apigateway/resources/:id/cors', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const result = await svc(c).enableApiGatewayCors(cloud, c.req.param('id'), { resourceId: typeof input.resourceId === 'string' ? input.resourceId : '', stage: typeof input.stage === 'string' ? input.stage : '', origin: typeof input.origin === 'string' ? input.origin : '', headers: typeof input.headers === 'string' ? input.headers : '', methods: Array.isArray(input.methods) ? input.methods.filter((method): method is string => typeof method === 'string') : [], }) return c.json(result) }) }) app.post('/:cloud/services/apigateway/resources/:id/routes/test', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const result = await svc(c).testApiGatewayRoute(cloud, c.req.param('id'), { path: typeof input.path === 'string' ? input.path : '', method: typeof input.method === 'string' ? input.method : '', stage: typeof input.stage === 'string' ? input.stage : '', body: typeof input.body === 'string' ? input.body : '', headers: stringHeaders(input.headers), query: typeof input.query === 'string' ? input.query : '', }) return c.json(result) }) }) app.post('/:cloud/services/apigateway/resources/:id/methods/test', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const result = await svc(c).testApiGatewayMethod(cloud, c.req.param('id'), { resourceId: typeof input.resourceId === 'string' ? input.resourceId : '', httpMethod: typeof input.httpMethod === 'string' ? input.httpMethod : '', pathWithQueryString: typeof input.pathWithQueryString === 'string' ? input.pathWithQueryString : '', body: typeof input.body === 'string' ? input.body : '', headers: stringHeaders(input.headers), multiValueHeaders: stringArrayHeaders(input.multiValueHeaders), stageVariables: stringHeaders(input.stageVariables), }) return c.json(result) }) }) app.get('/:cloud/services/storage/resources/:id/lambda-triggers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listS3LambdaTriggers(cloud, c.req.param('id')))) }) app.post('/:cloud/services/storage/resources/:id/lambda-triggers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const trigger = await svc(c).createS3LambdaTrigger(cloud, c.req.param('id'), { functionName: typeof input.functionName === 'string' ? input.functionName : '', events: Array.isArray(input.events) ? input.events.filter((event): event is string => typeof event === 'string') : [], prefix: typeof input.prefix === 'string' ? input.prefix : undefined, suffix: typeof input.suffix === 'string' ? input.suffix : undefined, }) return c.json(trigger, 201) }) }) app.delete('/:cloud/services/storage/resources/:id/lambda-triggers/:triggerId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteS3LambdaTrigger(cloud, c.req.param('id'), c.req.param('triggerId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/storage/resources/:id/website', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).getS3BucketWebsite(cloud, c.req.param('id')))) }) app.put('/:cloud/services/storage/resources/:id/website', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await jsonBody<{mode?: unknown; indexDocument?: unknown; errorDocument?: unknown; hostName?: unknown; protocol?: unknown}>(c) const configuration = s3WebsiteConfiguration(input) await svc(c).putS3BucketWebsite(cloud, c.req.param('id'), configuration) return c.json({ok: true}) }) }) app.delete('/:cloud/services/storage/resources/:id/website', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteS3BucketWebsite(cloud, c.req.param('id')) return c.json({ok: true}) }) }) app.get('/:cloud/services/serverless/resources/:id/eventbridge-triggers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listEventBridgeLambdaTriggers(cloud, c.req.param('id')))) }) app.post('/:cloud/services/serverless/resources/:id/eventbridge-triggers', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const trigger = await svc(c).createEventBridgeLambdaTrigger(cloud, { ruleName: typeof input.ruleName === 'string' ? input.ruleName : '', scheduleExpression: typeof input.scheduleExpression === 'string' ? input.scheduleExpression : '', functionName: c.req.param('id'), }) return c.json(trigger, 201) }) }) app.delete('/:cloud/services/serverless/resources/:id/eventbridge-triggers/:triggerId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteEventBridgeLambdaTrigger(cloud, c.req.param('id'), c.req.param('triggerId')) return c.json({ok: true}) }) }) app.get('/:cloud/services/events/resources/:id/rules', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listEventBridgeRules(cloud, c.req.param('id')))) }) app.post('/:cloud/services/events/resources/:id/events', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const result = await svc(c).putEventBridgeEvent(cloud, c.req.param('id'), { source: typeof input.source === 'string' ? input.source : '', detailType: typeof input.detailType === 'string' ? input.detailType : '', detail: typeof input.detail === 'string' ? input.detail : '', resources: Array.isArray(input.resources) ? input.resources.filter((resource): resource is string => typeof resource === 'string') : undefined, }) return c.json(result, result.errorCode ? 400 : 201) }) }) app.get('/:cloud/services/iac/resources/:id/change-sets', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listCloudFormationChangeSets(cloud, c.req.param('id')))) }) app.post('/:cloud/services/iac/resources/:id/change-sets', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json<{name?: string; templateBody?: string; parameters?: Record; capabilities?: string[]}>(); return c.json(await svc(c).createCloudFormationChangeSet(cloud, c.req.param('id'), {name: input.name ?? '', templateBody: input.templateBody ?? '', parameters: input.parameters, capabilities: input.capabilities}), 201) }) }) app.post('/:cloud/services/iac/resources/:id/change-sets/:name/execute', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).executeCloudFormationChangeSet(cloud, c.req.param('id'), c.req.param('name')); return c.json({ok: true}) }) }) app.delete('/:cloud/services/iac/resources/:id/change-sets/:name', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteCloudFormationChangeSet(cloud, c.req.param('id'), c.req.param('name')); return c.json({ok: true}) }) }) app.put('/:cloud/services/iac/resources/:id/termination-protection', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json<{enabled?: boolean}>(); await svc(c).updateCloudFormationTerminationProtection(cloud, c.req.param('id'), input.enabled === true); return c.json({ok: true}) }) }) app.post('/:cloud/services/events/resources/:id/rules', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const rule = await svc(c).createEventBridgeRule(cloud, c.req.param('id'), { name: typeof input.name === 'string' ? input.name : '', scheduleExpression: typeof input.scheduleExpression === 'string' ? input.scheduleExpression : undefined, eventPattern: typeof input.eventPattern === 'string' ? input.eventPattern : undefined, state: input.state === 'DISABLED' ? 'DISABLED' : input.state === 'ENABLED' ? 'ENABLED' : undefined, description: typeof input.description === 'string' ? input.description : undefined, targets: Array.isArray(input.targets) ? input.targets.flatMap((target) => { if (!target || typeof target !== 'object' || Array.isArray(target)) return [] const value = target as {id?: unknown; arn?: unknown} return typeof value.id === 'string' && typeof value.arn === 'string' ? [{id: value.id, arn: value.arn}] : [] }) : [], }) return c.json(rule, 201) }) }) app.delete('/:cloud/services/events/resources/:id/rules/:ruleName', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteEventBridgeRule(cloud, c.req.param('id'), c.req.param('ruleName')) return c.json({ok: true}) }) }) app.post('/:cloud/services/workflows/resources/:id/executions', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{input?: unknown; name?: unknown}>(c) if (typeof body.input !== 'string') throw new ValidationError('input must be a JSON string') try { JSON.parse(body.input) } catch { throw new ValidationError('input must be valid JSON') } if (body.name !== undefined && (typeof body.name !== 'string' || !body.name.trim())) throw new ValidationError('name must be a non-empty string') return c.json(await svc(c).startWorkflowExecution('aws', c.req.param('id'), {input: body.input, ...(typeof body.name === 'string' ? {name: body.name} : {})}), 201) }) }) app.get('/:cloud/services/workflows/resources/:id/executions', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listWorkflowExecutions('aws', c.req.param('id')))) }) app.get('/:cloud/services/workflows/resources/:id/executions/:executionArn', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).describeWorkflowExecution('aws', c.req.param('executionArn')))) }) app.get('/:cloud/services/workflows/resources/:id/executions/:executionArn/history', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).getWorkflowExecutionHistory('aws', c.req.param('executionArn'), c.req.query('nextToken')))) }) app.post('/:cloud/services/workflows/resources/:id/executions/:executionArn/stop', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).stopWorkflowExecution('aws', c.req.param('executionArn')) return c.json({ok: true}) }) }) app.post('/:cloud/services/codebuild/resources/:id/builds', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{environmentVariablesOverride?: unknown; buildspecOverride?: unknown}>(c) const overrides = body.environmentVariablesOverride if (overrides !== undefined && (!Array.isArray(overrides) || overrides.some((item) => !item || typeof item !== 'object' || typeof (item as {name?: unknown}).name !== 'string' || typeof (item as {value?: unknown}).value !== 'string' || ((item as {type?: unknown}).type !== undefined && !['PLAINTEXT', 'PARAMETER_STORE', 'SECRETS_MANAGER'].includes((item as {type: string}).type))))) { throw new ValidationError('environmentVariablesOverride must contain name, value, and an optional supported type') } if (body.buildspecOverride !== undefined && typeof body.buildspecOverride !== 'string') { throw new ValidationError('buildspecOverride must be a string') } const buildspecOverride = typeof body.buildspecOverride === 'string' && body.buildspecOverride.trim() ? body.buildspecOverride : undefined return c.json(await svc(c).startCodeBuild('aws', c.req.param('id'), {environmentVariablesOverride: overrides as never, buildspecOverride}), 201) }) }) app.get('/:cloud/services/codebuild/resources/:id/builds', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listCodeBuilds('aws', c.req.param('id')))) }) app.get('/:cloud/services/codebuild/resources/:id/builds/:buildId', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const build = await svc(c).getCodeBuild('aws', c.req.param('buildId')) return build ? c.json(build) : c.json({error: 'Build not found'}, 404) }) }) app.post('/:cloud/services/codepipeline/resources/:id/executions', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).startPipeline('aws', c.req.param('id')), 201)) }) app.post('/:cloud/services/codedeploy/resources/:id/deployments', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{functionName?: unknown; alias?: unknown; currentVersion?: unknown; targetVersion?: unknown; beforeAllowTrafficHook?: unknown; afterAllowTrafficHook?: unknown}>(c) const [applicationName, deploymentGroupName] = codeDeployGroupId(c.req.param('id')) if (typeof body.functionName !== 'string' || typeof body.alias !== 'string' || typeof body.currentVersion !== 'string' || typeof body.targetVersion !== 'string') throw new ValidationError('functionName, alias, currentVersion, and targetVersion are required') return c.json(await svc(c).startCodeDeployDeployment('aws', applicationName, deploymentGroupName, {functionName: body.functionName, alias: body.alias, currentVersion: body.currentVersion, targetVersion: body.targetVersion, beforeAllowTrafficHook: typeof body.beforeAllowTrafficHook === 'string' ? body.beforeAllowTrafficHook : undefined, afterAllowTrafficHook: typeof body.afterAllowTrafficHook === 'string' ? body.afterAllowTrafficHook : undefined}), 201) }) }) app.get('/:cloud/services/codedeploy/resources/:id/deployments', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const [applicationName, deploymentGroupName] = codeDeployGroupId(c.req.param('id')); return c.json(await svc(c).listCodeDeployDeployments('aws', applicationName, deploymentGroupName)) }) }) app.get('/:cloud/services/codedeploy/deployments/:deploymentId', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).getCodeDeployDeployment('aws', c.req.param('deploymentId')))) }) app.post('/:cloud/services/codedeploy/deployments/:deploymentId/stop', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{rollback?: unknown}>(c); await svc(c).stopCodeDeployDeployment('aws', c.req.param('deploymentId'), body.rollback === true); return c.json({ok: true}) }) }) app.get('/:cloud/services/codepipeline/resources/:id/executions', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listPipelineExecutions('aws', c.req.param('id')))) }) app.get('/:cloud/services/codepipeline/resources/:id/state', async (c) => { if (c.req.param('cloud') !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).getPipelineState('aws', c.req.param('id')))) }) app.get('/:cloud/services/serverless/resources/:id/sqs-event-source-mappings', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listSqsLambdaEventSourceMappings(cloud, c.req.param('id')))) }) app.post('/:cloud/services/serverless/resources/:id/sqs-event-source-mappings', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await c.req.json>() const trigger = await svc(c).createSqsLambdaEventSourceMapping(cloud, { functionName: c.req.param('id'), queueArn: typeof input.queueArn === 'string' ? input.queueArn : '', batchSize: typeof input.batchSize === 'number' ? input.batchSize : undefined, }) return c.json(trigger, 201) }) }) app.delete('/:cloud/services/serverless/resources/:id/sqs-event-source-mappings/:mappingId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteSqsLambdaEventSourceMapping(cloud, c.req.param('id'), c.req.param('mappingId')) return c.json({ok: true}) }) }) app.post('/:cloud/services/kinesis/resources/:id/records', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await jsonBody<{partitionKey?: unknown; data?: unknown}>(c) return c.json(await svc(c).putKinesisRecord(cloud, c.req.param('id'), { partitionKey: typeof input.partitionKey === 'string' ? input.partitionKey : '', data: typeof input.data === 'string' ? input.data : '', }), 201) }) }) app.get('/:cloud/services/kinesis/resources/:id/records', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listKinesisRecords(cloud, c.req.param('id')))) }) app.get('/:cloud/services/serverless/event-source-mappings', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => c.json(await svc(c).listLambdaEventSourceMappings(cloud, { functionName: c.req.query('functionName'), eventSourceArn: c.req.query('eventSourceArn'), }))) }) app.post('/:cloud/services/serverless/event-source-mappings', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await jsonBody<{functionName?: unknown; eventSourceArn?: unknown; batchSize?: unknown; startingPosition?: unknown}>(c) return c.json(await svc(c).createLambdaEventSourceMapping(cloud, { functionName: typeof input.functionName === 'string' ? input.functionName : '', eventSourceArn: typeof input.eventSourceArn === 'string' ? input.eventSourceArn : '', batchSize: typeof input.batchSize === 'number' ? input.batchSize : undefined, startingPosition: input.startingPosition === 'TRIM_HORIZON' || input.startingPosition === 'LATEST' ? input.startingPosition : undefined, }), 201) }) }) app.patch('/:cloud/services/serverless/event-source-mappings/:mappingId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const input = await jsonBody<{batchSize?: unknown; enabled?: unknown}>(c) return c.json(await svc(c).updateLambdaEventSourceMapping(cloud, c.req.param('mappingId'), { batchSize: typeof input.batchSize === 'number' ? input.batchSize : undefined, enabled: typeof input.enabled === 'boolean' ? input.enabled : undefined, })) }) }) app.delete('/:cloud/services/serverless/event-source-mappings/:mappingId', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (cloud !== 'aws') return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteLambdaEventSourceMapping(cloud, c.req.param('mappingId')); return c.json({ok: true}) }) }) app.get('/:cloud/services/:service/resources/:id/collections', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const page = await svc(c).listChildCollections(target.cloud, target.service, c.req.param('id'), pageQuery(c)) return c.json(page) }) }) app.post('/:cloud/services/:service/resources/:id/collections', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() const collection = await svc(c).createChildCollection(target.cloud, target.service, c.req.param('id'), {values}) return c.json(collection, 201) }) }) app.delete('/:cloud/services/:service/resources/:id/collections/:cid', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteChildCollection(target.cloud, target.service, c.req.param('id'), c.req.param('cid')) return c.json({ok: true}) }) }) app.get('/:cloud/services/:service/resources/:id/collections/:cid/items', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const page = await svc(c).listCollectionItems(target.cloud, target.service, c.req.param('id'), c.req.param('cid'), pageQuery(c)) return c.json(page) }) }) app.post('/:cloud/services/:service/resources/:id/collections/:cid/items', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json>() const item = await svc(c).putCollectionItem(target.cloud, target.service, c.req.param('id'), c.req.param('cid'), body) return c.json(item, 201) }) }) app.delete('/:cloud/services/:service/resources/:id/collections/:cid/items/:itemId', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteCollectionItem(target.cloud, target.service, c.req.param('id'), c.req.param('cid'), c.req.param('itemId'), c.req.query('partitionKey') ?? null) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/collections/:cid/query', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{query?: string | Record}>() const page = await svc(c).queryCollectionItems(target.cloud, target.service, c.req.param('id'), c.req.param('cid'), body.query ?? '', pageQuery(c)) return c.json(page) }) }) app.get('/:cloud/services/:service/resources/:id/items', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const page = await svc(c).listFlatItems(target.cloud, target.service, c.req.param('id'), pageQuery(c)) return c.json(page) }) }) app.post('/:cloud/services/:service/resources/:id/items', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json>() const item = await svc(c).putFlatItem(target.cloud, target.service, c.req.param('id'), body) return c.json(item, 201) }) }) app.get('/:cloud/services/:service/resources/:id/items/:itemId', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const item = await svc(c).getFlatItem(target.cloud, target.service, c.req.param('id'), c.req.param('itemId')) return item ? c.json(item) : c.json({error: 'Item not found'}, 404) }) }) app.delete('/:cloud/services/:service/resources/:id/items/:itemId', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteFlatItem(target.cloud, target.service, c.req.param('id'), c.req.param('itemId'), c.req.query('partitionKey') ?? null) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/query', async (c) => { const target = childTarget(c) if (!target) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const body = await c.req.json<{query?: string | Record}>() const page = await svc(c).queryFlatItems(target.cloud, target.service, c.req.param('id'), body.query ?? '', pageQuery(c)) return c.json(page) }) }) app.get('/:cloud/services/:service/resources/:id', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const resource = await svc(c).getResource(cloud, serviceType, c.req.param('id')) if (!resource) return c.json({error: 'Resource not found'}, 404) return c.json(resource) }) }) app.get('/:cloud/services/:service/resources/:id/objects', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const objects = await svc(c).listObjects(cloud, serviceType, c.req.param('id'), c.req.query('prefix') ?? '') return c.json(objects) }) }) app.put('/:cloud/services/:service/resources/:id/object', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) const key = c.req.query('key') ?? '' if (!key) return c.json({error: 'Object key is required'}, 400) const body = new Uint8Array(await c.req.arrayBuffer()) const contentType = c.req.header('content-type') ?? 'application/octet-stream' return withRuntime(c, async () => { await svc(c).putObject(cloud, serviceType, c.req.param('id'), key, body, contentType) return c.json({ok: true}) }) }) app.get('/:cloud/services/:service/resources/:id/object', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) const key = c.req.query('key') ?? '' if (!key) return c.json({error: 'Object key is required'}, 400) return withRuntime(c, async () => { const object = await svc(c).getObject(cloud, serviceType, c.req.param('id'), key) return new Response(object.body, { headers: { 'content-type': object.contentType, ...(object.contentLength === null ? {} : {'content-length': String(object.contentLength)}), 'content-disposition': `attachment; filename="${key.split('/').pop() ?? key}"`, }, }) }) }) app.delete('/:cloud/services/:service/resources/:id/object', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) const key = c.req.query('key') ?? '' if (!key) return c.json({error: 'Object key is required'}, 400) return withRuntime(c, async () => { await svc(c).deleteObject(cloud, serviceType, c.req.param('id'), key) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/object/copy', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) const {srcKey, destKey, destResourceId} = await c.req.json<{srcKey: string; destKey: string; destResourceId?: string}>() if (!srcKey || !destKey) return c.json({error: 'srcKey and destKey are required'}, 400) return withRuntime(c, async () => { await svc(c).copyObject(cloud, serviceType, c.req.param('id'), srcKey, destKey, destResourceId) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/invoke', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) { return c.json({error: 'Unknown cloud or service'}, 404) } return withRuntime(c, async () => { const body: {payload?: string} = await c.req.json<{payload?: string}>().catch(() => ({})) const result = await svc(c).invokeResource( cloud, serviceType, c.req.param('id'), body.payload ?? '{}', ) return c.json(result) }) }) app.post('/:cloud/services/:service/resources/:id/start', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).startResource(cloud, serviceType, c.req.param('id')) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/stop', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).stopResource(cloud, serviceType, c.req.param('id')) return c.json({ok: true}) }) }) app.post('/:cloud/services/:service/resources/:id/reboot', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).rebootResource(cloud, serviceType, c.req.param('id')) return c.json({ok: true}) }) }) app.post('/:cloud/services/notifications/resources/:id/publish', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{message?: unknown; subject?: unknown; messageAttributes?: unknown}>(c) if (typeof body.message !== 'string' || !body.message) { throw new ValidationError('message is required') } if (body.subject !== undefined && typeof body.subject !== 'string') { throw new ValidationError('subject must be a string') } if (body.messageAttributes !== undefined && !isStringRecord(body.messageAttributes)) { throw new ValidationError('messageAttributes must be an object of string values') } const result = await svc(c).publishNotification(cloud, c.req.param('id'), { message: body.message, ...(body.subject ? {subject: body.subject} : {}), ...(body.messageAttributes ? {messageAttributes: body.messageAttributes} : {}), }) return c.json(result) }) }) app.get('/:cloud/services/notifications/resources/:id/subscriptions', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => c.json(await svc(c).listSnsSubscriptions(cloud, c.req.param('id')))) }) app.post('/:cloud/services/notifications/resources/:id/subscriptions', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { const body = await jsonBody<{protocol?: unknown; endpoint?: unknown}>(c) if (typeof body.protocol !== 'string' || !body.protocol.trim()) throw new ValidationError('protocol is required') if (typeof body.endpoint !== 'string' || !body.endpoint.trim()) throw new ValidationError('endpoint is required') const subscription = await svc(c).createSnsSubscription(cloud, c.req.param('id'), { protocol: body.protocol, endpoint: body.endpoint, }) return c.json(subscription, 201) }) }) app.delete('/:cloud/services/notifications/resources/:id/subscriptions/:subscriptionArn', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withRuntime(c, async () => { await svc(c).deleteSnsSubscription(cloud, c.req.param('subscriptionArn')) return c.json({ok: true}) }) }) app.post('/:cloud/services/kms/resources/:id/encrypt', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withSensitiveRuntime(c, async () => { const body = await jsonBody(c) const result = await svc(c).encryptKms(cloud, c.req.param('id'), { plaintext: decodeBase64(body.plaintextBase64, 'plaintextBase64', MAX_PLAINTEXT_BASE64_LENGTH), encryptionAlgorithm: encryptionAlgorithm(body.encryptionAlgorithm), encryptionContext: encryptionContext(body.encryptionContext), }) return c.json({ ciphertextBlobBase64: Buffer.from(result.ciphertextBlob).toString('base64'), keyId: result.keyId, encryptionAlgorithm: result.encryptionAlgorithm, }) }) }) app.post('/:cloud/services/kms/resources/:id/decrypt', async (c) => { const cloud = c.req.param('cloud') as CloudProvider if (!isCloudProvider(cloud)) return c.json({error: 'Unknown cloud'}, 404) return withSensitiveRuntime(c, async () => { const body = await jsonBody(c) const result = await svc(c).decryptKms(cloud, c.req.param('id'), { ciphertextBlob: decodeBase64( body.ciphertextBlobBase64, 'ciphertextBlobBase64', MAX_CIPHERTEXT_BASE64_LENGTH, ), encryptionAlgorithm: encryptionAlgorithm(body.encryptionAlgorithm), encryptionContext: encryptionContext(body.encryptionContext), }) return c.json({ plaintextBase64: Buffer.from(result.plaintext).toString('base64'), keyId: result.keyId, encryptionAlgorithm: result.encryptionAlgorithm, }) }) }) app.post('/:cloud/services/:service/resources', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() const resource = await svc(c).createResource(cloud, serviceType, {values}) return c.json(resource, 201) }) }) app.patch('/:cloud/services/:service/resources/:id', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { const values = await c.req.json>() const resource = await svc(c).updateResource(cloud, serviceType, c.req.param('id'), {values}) return c.json(resource, 200) }) }) app.delete('/:cloud/services/:service/resources/:id', async (c) => { const cloud = c.req.param('cloud') as CloudProvider const serviceType = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(serviceType)) return c.json({error: 'Unknown cloud or service'}, 404) return withRuntime(c, async () => { await svc(c).deleteResource(cloud, serviceType, c.req.param('id')) return c.json({ok: true}) }) }) return app } function isCloudProvider(value: string): value is CloudProvider { return value === 'aws' || value === 'azure' || value === 'gcp' } function s3WebsiteConfiguration(input: {mode?: unknown; indexDocument?: unknown; errorDocument?: unknown; hostName?: unknown; protocol?: unknown}): S3BucketWebsiteConfiguration { if (input.mode === 'index') { if (typeof input.indexDocument !== 'string' || !input.indexDocument.trim()) { throw new ValidationError('indexDocument is required for index website hosting') } if (input.errorDocument !== undefined && typeof input.errorDocument !== 'string' && input.errorDocument !== null) { throw new ValidationError('errorDocument must be a string or null') } return { mode: 'index', indexDocument: input.indexDocument.trim(), errorDocument: typeof input.errorDocument === 'string' && input.errorDocument.trim() ? input.errorDocument.trim() : null, } } if (input.mode === 'redirect') { if (typeof input.hostName !== 'string' || !input.hostName.trim()) { throw new ValidationError('hostName is required for redirect website hosting') } if (input.protocol !== undefined && input.protocol !== null && input.protocol !== 'http' && input.protocol !== 'https') { throw new ValidationError('protocol must be http, https, or null') } return { mode: 'redirect', hostName: input.hostName.trim(), protocol: input.protocol === 'http' || input.protocol === 'https' ? input.protocol : null, } } throw new ValidationError('mode must be index or redirect') } async function jsonBody(c: Context): Promise { try { const body = await c.req.json() if (!body || typeof body !== 'object' || Array.isArray(body)) { throw new ValidationError('Request body must be a JSON object') } return body as T } catch (error) { if (error instanceof ValidationError) throw error throw new ValidationError('Request body must be valid JSON') } } function encryptionAlgorithm(value: unknown): KmsEncryptionAlgorithm { if (typeof value !== 'string' || !KMS_ENCRYPTION_ALGORITHMS.has(value as KmsEncryptionAlgorithm)) { throw new ValidationError('encryptionAlgorithm must be a supported KMS encryption algorithm') } return value as KmsEncryptionAlgorithm } function encryptionContext(value: unknown): Record | undefined { if (value === undefined) return undefined if (!value || typeof value !== 'object' || Array.isArray(value)) { throw new ValidationError('encryptionContext must be an object of string values') } const entries = Object.entries(value) if (entries.some(([, entryValue]) => typeof entryValue !== 'string')) { throw new ValidationError('encryptionContext must be an object of string values') } return entries.length === 0 ? undefined : Object.fromEntries(entries) as Record } function isStringRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.values(value).every((item) => typeof item === 'string') } function stringHeaders(value: unknown): Record { if (value === undefined) return {} if (!value || typeof value !== 'object' || Array.isArray(value)) { throw new ValidationError('headers must be an object of string values') } const entries = Object.entries(value) if (entries.some(([, headerValue]) => typeof headerValue !== 'string')) { throw new ValidationError('headers must be an object of string values') } return Object.fromEntries(entries) as Record } function stringArrayHeaders(value: unknown): Record { if (!value || typeof value !== 'object' || Array.isArray(value)) return {} return Object.fromEntries(Object.entries(value).flatMap(([name, values]) => Array.isArray(values) && values.every((header) => typeof header === 'string') ? [[name, values]] : [], )) } function decodeBase64(value: unknown, field: string, maxLength: number): Uint8Array { if (typeof value !== 'string') throw new ValidationError(`${field} must be a base64 string`) if (value.length > maxLength) throw new ValidationError(`${field} is too large`) if (value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) { throw new ValidationError(`${field} must be canonical base64`) } const decoded = Buffer.from(value, 'base64') if (decoded.toString('base64') !== value) throw new ValidationError(`${field} must be canonical base64`) return decoded } /** * Resolve and validate the cloud/service pair for a child-collection route. * Returns null when either is unknown, which the caller turns into a 404. */ function childTarget(c: Context): {cloud: CloudProvider; service: CloudServiceType} | null { const cloud = c.req.param('cloud') as CloudProvider const service = c.req.param('service') as CloudServiceType if (!isCloudProvider(cloud) || !isServiceType(service)) return null return {cloud, service} } /** Parse paging params. An out-of-range limit raises and becomes a 400. */ function pageQuery(c: Context): PageQuery { return {cursor: c.req.query('cursor'), limit: clampLimit(c.req.query('limit'))} } function codeDeployGroupId(id: string): [string, string] { const separator = id.indexOf('/') if (separator <= 0 || separator === id.length - 1) throw new ValidationError('Deployment group id must be applicationName/deploymentGroupName') return [id.slice(0, separator), id.slice(separator + 1)] } async function withRuntime(c: Context, handler: () => Promise): Promise { try { return await handler() } catch (err) { const {status, body} = toHttpError(err, mapAwsSdkError) return c.json(body, status) } } async function withSensitiveRuntime(c: Context, handler: () => Promise): Promise { c.header('cache-control', 'no-store') try { return await handler() } catch (err) { const {status, body} = toHttpError(err, mapAwsSdkError) if (err instanceof CloudError) return c.json(body, status) const message = body.code === 'invalid_request' ? 'KMS rejected the request' : body.error return c.json({error: message, code: body.code, message}, status) } } export default createCloudRoutes()