feat: Cloudformation + RDS

This commit is contained in:
Zakaria
2026-09-30 17:43:42 -04:00
parent 548a15d7ec
commit 7764ea0dc1
411 changed files with 10182 additions and 1063 deletions
@@ -8,6 +8,7 @@ import {useCloudServicesQuery, useCloudStatusQuery, useCloudsQuery} from '@/api/
import {CloudSelector} from '@/components/CloudSelector'
import {DynamicResourceView} from '@/components/DynamicResourceView'
import {EmptyState} from '@/components/EmptyState'
import {ServiceIcon} from '@/components/serviceIcons'
import {normalizeCapabilities, withRuntimeState, withServiceAvailability} from '@/lib/capabilities'
import type {CloudProvider, CloudServiceDescriptor, CloudServiceType, CloudStatus} from '@/types/cloud'
import type {ServiceSchema} from '@/types/schema'
@@ -53,7 +54,9 @@ export function CloudExplorerPage() {
<>
<div className="page-header cloud-explorer-header">
<div className="page-title">
<Cloud size={20}/>
{selectedService
? <ServiceIcon cloud={cloud} iconKey={selectedService.iconKey} size={32}/>
: <Cloud size={20}/>}
<div>
<h1>Cloud Explorer</h1>
<p className="muted">Unified local runtime console</p>
@@ -0,0 +1,72 @@
import {ArrowLeft} from 'lucide-react'
import {useMemo} from 'react'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {Link, useNavigate, useParams} from 'react-router-dom'
import {getCloudResource, listFlatItems, putFlatItem} from '@/api/cloudProxyClient'
import {useCloudServicesQuery, useCloudStatusQuery} from '@/api/queries/cloudQueries'
import {DynamoDbItemEditor} from '@/components/DynamoDbItemEditor'
import {useAccountId} from '@/lib/accountStore'
import {documentToAttributeValues, dynamoKeyFields} from '@/lib/dynamodbItem'
import type {CloudProvider} from '@/types/cloud'
export function DynamoDbItemEditorPage() {
const {cloud, resourceId, itemId} = useParams()
const accountId = useAccountId()
const navigate = useNavigate()
const queryClient = useQueryClient()
const isCreate = !itemId
const validCloud = cloud === 'aws' || cloud === 'azure' || cloud === 'gcp'
const servicesQuery = useCloudServicesQuery(validCloud ? cloud : 'aws')
const statusQuery = useCloudStatusQuery(validCloud ? cloud : 'aws')
const resourceQuery = useQuery({
queryKey: ['cloud-data-resource', accountId, cloud, 'nosql', resourceId],
queryFn: ({signal}) => getCloudResource(cloud as CloudProvider, 'nosql', resourceId ?? '', signal),
enabled: validCloud && Boolean(resourceId) && statusQuery.data?.runtime === 'reachable',
})
const itemsKey = useMemo(() => ['nosql-items', accountId, cloud, resourceId], [accountId, cloud, resourceId])
const itemsQuery = useQuery({
queryKey: itemsKey,
queryFn: ({signal}) => listFlatItems(cloud as CloudProvider, 'nosql', resourceId ?? '', undefined, signal),
enabled: validCloud && Boolean(resourceId) && statusQuery.data?.runtime === 'reachable' && !isCreate,
})
const destination = validCloud && resourceId ? `/cloud-explorer/${cloud}/nosql/${encodeURIComponent(resourceId)}/data` : '/console/aws'
const descriptor = servicesQuery.data?.find((candidate) => candidate.service === 'nosql')
const resource = resourceQuery.data
const keyFields = useMemo(() => dynamoKeyFields(resource), [resource])
const item = itemsQuery.data?.items.find((candidate) => candidate.id === itemId)
const initialDocument = useMemo(() => item ? documentToAttributeValues(item.body, keyFields) : undefined, [item, keyFields])
const putItemMutation = useMutation({
mutationFn: (document: Record<string, unknown>) => putFlatItem(cloud as CloudProvider, 'nosql', resourceId ?? '', document),
onSuccess: () => {
void queryClient.invalidateQueries({queryKey: itemsKey})
navigate(destination)
},
})
if (!validCloud || !resourceId) return <EditorNotice title="Invalid item editor" detail="Check the cloud and table in this link."/>
if (servicesQuery.isPending || statusQuery.isPending || resourceQuery.isPending || (!isCreate && itemsQuery.isPending)) return <EditorNotice title="Loading item editor" detail="Loading the table and item data."/>
if (servicesQuery.isError || statusQuery.isError || resourceQuery.isError || itemsQuery.isError) return <EditorNotice title="Unable to load item editor" detail={(servicesQuery.error ?? statusQuery.error ?? resourceQuery.error ?? itemsQuery.error)?.message ?? 'The table or item data could not be loaded.'}/>
if (!descriptor || descriptor.availability !== 'available') return <EditorNotice title="Service unavailable" detail={descriptor?.reason ?? 'DynamoDB item editing is unavailable for this runtime.'}/>
if (statusQuery.data?.runtime !== 'reachable') return <EditorNotice title="Runtime unavailable" detail={statusQuery.data?.error ?? 'Start the selected runtime to edit DynamoDB items.'}/>
if (!resource) return <EditorNotice title="Table not found" detail="The DynamoDB table no longer exists."/>
if (!isCreate && !item) return <EditorNotice title="Item not found" detail="The selected DynamoDB item no longer exists."/>
return (
<div className="dynamodb-item-editor-page">
<Link className="button compact dynamodb-item-editor-back" to={destination}><ArrowLeft size={14}/>Back to table data</Link>
<DynamoDbItemEditor
editing={!isCreate}
keyFields={keyFields}
initialDocument={initialDocument}
isPending={putItemMutation.isPending}
submitError={putItemMutation.error instanceof Error ? putItemMutation.error.message : undefined}
onClose={() => navigate(destination)}
onSubmit={(document) => putItemMutation.mutate(document)}
/>
</div>
)
}
function EditorNotice({title, detail}: {title: string; detail: string}) {
return <div className="content"><div className="runtime-notice" role="status"><h3>{title}</h3><p>{detail}</p></div></div>
}
@@ -1,7 +1,8 @@
import {ArrowLeft, Workflow} from 'lucide-react'
import {ArrowLeft} from 'lucide-react'
import {Link, useParams} from 'react-router-dom'
import {useCloudResourceQuery} from '@/api/queries/cloudQueries'
import {WorkflowStudio} from '@/components/WorkflowStudio'
import {ServiceIcon} from '@/components/serviceIcons'
export function WorkflowStudioPage() {
const {resourceId} = useParams()
@@ -11,7 +12,7 @@ export function WorkflowStudioPage() {
<header className="page-header data-workspace-header">
<Link className="button compact" to="/cloud-explorer/aws/workflows"><ArrowLeft size={14}/>Back to workflows</Link>
<div className="page-title">
<Workflow size={20}/>
<ServiceIcon cloud="aws" iconKey="workflows" size={32}/>
<div><h1>{resourceQuery.data?.name ?? resourceId ?? 'Workflow Studio'}</h1><p className="muted">AWS Step Functions · Workflow Studio</p></div>
</div>
</header>