feat: CORS enable button...ish still now understanding this

This commit is contained in:
Zakaria
2026-09-27 14:46:04 -04:00
parent 8470b8d17a
commit b474e823ce
17 changed files with 943 additions and 31 deletions
@@ -0,0 +1,108 @@
import {useEffect, useState} from 'react'
import {Globe, Loader2, RefreshCw, Trash2} from 'lucide-react'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {deleteS3BucketWebsite, getS3BucketWebsite, putS3BucketWebsite} from '@/api/cloudProxyClient'
import {useAccountId} from '@/lib/accountStore'
import type {CloudResource, S3BucketWebsiteConfiguration} from '@/types/resource'
interface S3BucketWebsitePanelProps {
resource?: CloudResource
runtimeReachable: boolean
}
export function S3BucketWebsitePanel({resource, runtimeReachable}: S3BucketWebsitePanelProps) {
const queryClient = useQueryClient()
const accountId = useAccountId()
const [mode, setMode] = useState<'index' | 'redirect'>('index')
const [indexDocument, setIndexDocument] = useState('index.html')
const [errorDocument, setErrorDocument] = useState('')
const [hostName, setHostName] = useState('')
const [protocol, setProtocol] = useState<'http' | 'https' | ''>('')
const [confirmDisable, setConfirmDisable] = useState(false)
const queryKey = ['s3-bucket-website', accountId, resource?.id]
const websiteQuery = useQuery({
queryKey,
queryFn: ({signal}) => getS3BucketWebsite('aws', resource?.id ?? '', signal),
enabled: Boolean(resource) && runtimeReachable,
})
useEffect(() => {
const configuration = websiteQuery.data
if (!configuration) {
setMode('index')
setIndexDocument('index.html')
setErrorDocument('')
setHostName('')
setProtocol('')
return
}
setMode(configuration.mode)
if (configuration.mode === 'index') {
setIndexDocument(configuration.indexDocument)
setErrorDocument(configuration.errorDocument ?? '')
} else {
setHostName(configuration.hostName)
setProtocol(configuration.protocol ?? '')
}
}, [websiteQuery.data, resource?.id])
const saveMutation = useMutation({
mutationFn: (configuration: S3BucketWebsiteConfiguration) => putS3BucketWebsite('aws', resource?.id ?? '', configuration),
onSuccess: () => {
void queryClient.invalidateQueries({queryKey})
},
})
const deleteMutation = useMutation({
mutationFn: () => deleteS3BucketWebsite('aws', resource?.id ?? ''),
onSuccess: () => {
setConfirmDisable(false)
void queryClient.setQueryData<S3BucketWebsiteConfiguration | null>(queryKey, null)
void queryClient.invalidateQueries({queryKey})
},
})
if (!resource || resource.service !== 'storage' || resource.type !== 'bucket') return null
const endpoint = `http://${resource.name}.s3-website-${resource.region ?? 'us-east-1'}.localhost:4566`
const configuration = mode === 'index'
? {mode, indexDocument: indexDocument.trim(), errorDocument: errorDocument.trim() || null}
: {mode, hostName: hostName.trim(), protocol: protocol || null}
const canSave = !saveMutation.isPending && (mode === 'index' ? Boolean(configuration.indexDocument) : Boolean(configuration.hostName))
return <section className="table-panel">
<div className="dynamic-stage-header">
<div>
<p className="eyebrow">Static website hosting</p>
<h3><Globe size={15}/> {websiteQuery.data ? 'Enabled' : 'Not configured'}</h3>
<p className="muted compact-text">Website endpoint: <a href={endpoint} target="_blank" rel="noreferrer"><code>{endpoint}</code></a></p>
</div>
<button className="icon-btn" type="button" aria-label="Refresh website configuration" onClick={() => websiteQuery.refetch()} disabled={websiteQuery.isFetching}>
{websiteQuery.isFetching ? <Loader2 size={13}/> : <RefreshCw size={13}/>}
</button>
</div>
{websiteQuery.isLoading && <p className="muted compact-text">Loading website configuration...</p>}
{websiteQuery.isError && <p className="error-text compact-text">{websiteQuery.error instanceof Error ? websiteQuery.error.message : 'Unable to load website configuration.'}</p>}
<form className="resource-create-inline" onSubmit={(event) => {
event.preventDefault()
if (canSave) saveMutation.mutate(configuration)
}}>
<label className="dynamic-field" htmlFor="s3-website-mode">
<span>Hosting mode</span>
<select id="s3-website-mode" className="input" value={mode} onChange={(event) => setMode(event.target.value as 'index' | 'redirect')} disabled={saveMutation.isPending}>
<option value="index">Index and error documents</option>
<option value="redirect">Redirect all requests</option>
</select>
</label>
{mode === 'index' ? <>
<label className="dynamic-field" htmlFor="s3-website-index-document"><span>Index document</span><input id="s3-website-index-document" className="input" value={indexDocument} onChange={(event) => setIndexDocument(event.target.value)} placeholder="index.html" required disabled={saveMutation.isPending}/></label>
<label className="dynamic-field" htmlFor="s3-website-error-document"><span>Error document</span><input id="s3-website-error-document" className="input" value={errorDocument} onChange={(event) => setErrorDocument(event.target.value)} placeholder="error.html" disabled={saveMutation.isPending}/><small>Optional.</small></label>
</> : <>
<label className="dynamic-field" htmlFor="s3-website-redirect-host"><span>Redirect host name</span><input id="s3-website-redirect-host" className="input" value={hostName} onChange={(event) => setHostName(event.target.value)} placeholder="www.example.com" required disabled={saveMutation.isPending}/></label>
<label className="dynamic-field" htmlFor="s3-website-redirect-protocol"><span>Redirect protocol</span><select id="s3-website-redirect-protocol" className="input" value={protocol} onChange={(event) => setProtocol(event.target.value as 'http' | 'https' | '')} disabled={saveMutation.isPending}><option value="">Preserve request protocol</option><option value="http">HTTP</option><option value="https">HTTPS</option></select></label>
</>}
<button className="button primary" type="submit" disabled={!canSave}>{saveMutation.isPending ? <Loader2 size={13}/> : null}{websiteQuery.data ? 'Save changes' : 'Enable website hosting'}</button>
</form>
{saveMutation.isError && <p className="error-text compact-text">{saveMutation.error instanceof Error ? saveMutation.error.message : 'Unable to save website configuration.'}</p>}
{websiteQuery.data && (confirmDisable ? <div className="apigateway-route-confirm"><span>Disable static website hosting?</span><button className="button danger compact" type="button" disabled={deleteMutation.isPending} onClick={() => deleteMutation.mutate()}>{deleteMutation.isPending ? 'Disabling' : 'Disable'}</button><button className="button compact" type="button" disabled={deleteMutation.isPending} onClick={() => setConfirmDisable(false)}>Cancel</button></div> : <button className="button danger compact" type="button" onClick={() => setConfirmDisable(true)}><Trash2 size={13}/> Disable website hosting</button>)}
{deleteMutation.isError && <p className="error-text compact-text">{deleteMutation.error instanceof Error ? deleteMutation.error.message : 'Unable to disable website hosting.'}</p>}
</section>
}