import {ExternalLink, RefreshCw} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useEffect, useState} from 'react' import {createDistributionInvalidation, getCloudResource, setDistributionEnabled} from '@/api/cloudProxyClient' import {CopyValueButton} from '@/components/CopyValueButton' import {useAccountId} from '@/lib/accountStore' import {formatDateTime} from '@/lib/format' import type {CloudResource} from '@/types/resource' type Props = { resource?: CloudResource runtimeReachable: boolean } type Origin = {id: string; domainName: string; kind: string; protocol: string | null} type Behavior = {pathPattern: string; originId: string; viewerProtocol: string; methods: string[]} type Invalidation = {id: string; status: string; createdAt: string | null} const VIEWER_PROTOCOL: Record = { 'allow-all': 'HTTP and HTTPS', 'redirect-to-https': 'Redirect to HTTPS', 'https-only': 'HTTPS only', } export function CloudFrontPanel({resource, runtimeReachable}: Props) { const accountId = useAccountId() const queryClient = useQueryClient() const [paths, setPaths] = useState('/*') const [notice, setNotice] = useState('') const distributionKey = ['cdn-distribution', accountId, resource?.id] // The table row is a list summary; behaviours and invalidations come from the full distribution. const distributionQuery = useQuery({ queryKey: distributionKey, queryFn: ({signal}) => getCloudResource('aws', 'cdn', resource?.id ?? '', signal), enabled: Boolean(resource) && runtimeReachable, }) useEffect(() => { setPaths('/*') setNotice('') }, [resource?.id]) const refresh = () => Promise.all([ queryClient.invalidateQueries({queryKey: distributionKey}), queryClient.invalidateQueries({queryKey: ['cloud-resources', 'aws', 'cdn']}), ]) const enabledMutation = useMutation({ mutationFn: (enabled: boolean) => setDistributionEnabled('aws', resource?.id ?? '', enabled), onSuccess: (updated) => { setNotice(updated.metadata.enabled ? 'Distribution enabled.' : 'Distribution disabled. It no longer answers viewer requests.') return refresh() }, }) const invalidationMutation = useMutation({ mutationFn: () => createDistributionInvalidation('aws', resource?.id ?? '', paths.split('\n')), onSuccess: (invalidation) => { setNotice(`Invalidation ${invalidation.id} ${invalidation.status === 'Completed' ? 'completed' : 'started'} for ${invalidation.paths.join(', ')}.`) return refresh() }, }) if (!resource) { return

Select a distribution

Choose a distribution to open it, change its state or invalidate cached files.

} const distribution = distributionQuery.data ?? resource const metadata = distribution.metadata const viewerUrl = typeof metadata.viewerUrl === 'string' ? metadata.viewerUrl : '' const enabled = metadata.enabled === true const origins = list(metadata.origins) const behaviors = list(metadata.behaviors) const invalidations = list(metadata.invalidations) const pending = enabledMutation.isPending || invalidationMutation.isPending const disabled = !runtimeReachable || pending const error = distributionQuery.error ?? enabledMutation.error ?? invalidationMutation.error return

Distribution

{enabled ? 'Enabled' : 'Disabled'} · {distribution.status ?? 'Unknown'}{metadata.defaultRootObject ? ` · serves ${String(metadata.defaultRootObject)} at /` : ''}
{viewerUrl &&
{viewerUrl} Open Served by TheCloud on this machine. On AWS the same distribution answers at {String(metadata.domainName ?? distribution.name)}.
}

Origins

{origins.length > 0 &&
{origins.map((origin) => ( ))}
OriginDomainType
{origin.id}{origin.domainName}{origin.kind === 's3' ? 'S3 bucket' : `Custom (${origin.protocol ?? 'https-only'})`}
} {behaviors.length > 0 && <>

Cache behaviours

The first path pattern that matches a request decides how it is served.
{behaviors.map((behavior) => ( ))}
Path patternOriginViewer protocolMethods
{behavior.pathPattern}{behavior.originId}{VIEWER_PROTOCOL[behavior.viewerProtocol] ?? behavior.viewerProtocol}{behavior.methods.join(', ')}
}

Invalidations

Clear cached copies so the next request fetches them from the origin.
{ event.preventDefault(); setNotice(''); invalidationMutation.mutate() }}>