style: format all source files with Prettier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DevContainer User
2026-03-04 00:55:42 +00:00
parent 8390aeb5df
commit b0ad4e3102
14 changed files with 255 additions and 197 deletions
+9 -3
View File
@@ -80,7 +80,9 @@ export function DecryptDialog({ sealedSecret, secretKey, onClose }: DecryptDialo
</Typography> </Typography>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose} aria-label="Close dialog">Close</Button> <Button onClick={onClose} aria-label="Close dialog">
Close
</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
@@ -103,7 +105,9 @@ export function DecryptDialog({ sealedSecret, secretKey, onClose }: DecryptDialo
</Typography> </Typography>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose} aria-label="Close dialog">Close</Button> <Button onClick={onClose} aria-label="Close dialog">
Close
</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
@@ -182,7 +186,9 @@ export function DecryptDialog({ sealedSecret, secretKey, onClose }: DecryptDialo
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose} aria-label="Close dialog">Close</Button> <Button onClick={onClose} aria-label="Close dialog">
Close
</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
+11 -5
View File
@@ -83,10 +83,12 @@ export function EncryptDialog({ open, onClose }: EncryptDialogProps) {
const handleCreate = async () => { const handleCreate = async () => {
// Filter out empty rows // Filter out empty rows
const validKeyValues = keyValues.filter(kv => kv.key || kv.value).map(kv => ({ const validKeyValues = keyValues
key: kv.key, .filter(kv => kv.key || kv.value)
value: kv.value, .map(kv => ({
})); key: kv.key,
value: kv.value,
}));
// Use the encryption hook // Use the encryption hook
const result = await encrypt({ const result = await encrypt({
@@ -232,7 +234,11 @@ export function EncryptDialog({ open, onClose }: EncryptDialogProps) {
disabled={keyValues.length === 1} disabled={keyValues.length === 1}
color="error" color="error"
aria-label={`Remove key-value pair ${index + 1}`} aria-label={`Remove key-value pair ${index + 1}`}
title={keyValues.length === 1 ? 'At least one key-value pair is required' : `Remove key-value pair ${index + 1}`} title={
keyValues.length === 1
? 'At least one key-value pair is required'
: `Remove key-value pair ${index + 1}`
}
> >
<Icon icon="mdi:delete" /> <Icon icon="mdi:delete" />
</IconButton> </IconButton>
+2 -2
View File
@@ -192,8 +192,8 @@ export class GenericErrorBoundary extends BaseErrorBoundary {
Something Went Wrong Something Went Wrong
</Typography> </Typography>
<Typography variant="body2" paragraph> <Typography variant="body2" paragraph>
An unexpected error occurred. Please try reloading the page or contact your administrator An unexpected error occurred. Please try reloading the page or contact your
if the problem persists. administrator if the problem persists.
</Typography> </Typography>
{this.state.error && ( {this.state.error && (
<Typography <Typography
+46 -7
View File
@@ -41,15 +41,37 @@ export function SealedSecretDetailSkeleton() {
<Skeleton variant="text" width="40%" height={40} sx={{ mb: 3 }} animation="wave" /> <Skeleton variant="text" width="40%" height={40} sx={{ mb: 3 }} animation="wave" />
{/* Metadata section */} {/* Metadata section */}
<Skeleton variant="rectangular" height={200} sx={{ mb: 2, borderRadius: 1 }} animation="wave" /> <Skeleton
variant="rectangular"
height={200}
sx={{ mb: 2, borderRadius: 1 }}
animation="wave"
/>
{/* Encrypted data section */} {/* Encrypted data section */}
<Skeleton variant="rectangular" height={150} sx={{ mb: 2, borderRadius: 1 }} animation="wave" /> <Skeleton
variant="rectangular"
height={150}
sx={{ mb: 2, borderRadius: 1 }}
animation="wave"
/>
{/* Actions section */} {/* Actions section */}
<Box sx={{ display: 'flex', gap: 2 }}> <Box sx={{ display: 'flex', gap: 2 }}>
<Skeleton variant="rectangular" width={120} height={36} sx={{ borderRadius: 1 }} animation="wave" /> <Skeleton
<Skeleton variant="rectangular" width={120} height={36} sx={{ borderRadius: 1 }} animation="wave" /> variant="rectangular"
width={120}
height={36}
sx={{ borderRadius: 1 }}
animation="wave"
/>
<Skeleton
variant="rectangular"
width={120}
height={36}
sx={{ borderRadius: 1 }}
animation="wave"
/>
</Box> </Box>
</Box> </Box>
); );
@@ -66,10 +88,27 @@ export function SealingKeysListSkeleton() {
{[1, 2].map(i => ( {[1, 2].map(i => (
<Box key={i} sx={{ mb: 3 }}> <Box key={i} sx={{ mb: 3 }}>
<Skeleton variant="text" width="30%" height={32} sx={{ mb: 1 }} animation="wave" /> <Skeleton variant="text" width="30%" height={32} sx={{ mb: 1 }} animation="wave" />
<Skeleton variant="rectangular" height={100} sx={{ borderRadius: 1, mb: 1 }} animation="wave" /> <Skeleton
variant="rectangular"
height={100}
sx={{ borderRadius: 1, mb: 1 }}
animation="wave"
/>
<Box sx={{ display: 'flex', gap: 1 }}> <Box sx={{ display: 'flex', gap: 1 }}>
<Skeleton variant="rectangular" width={100} height={28} sx={{ borderRadius: 1 }} animation="wave" /> <Skeleton
<Skeleton variant="rectangular" width={100} height={28} sx={{ borderRadius: 1 }} animation="wave" /> variant="rectangular"
width={100}
height={28}
sx={{ borderRadius: 1 }}
animation="wave"
/>
<Skeleton
variant="rectangular"
width={100}
height={28}
sx={{ borderRadius: 1 }}
animation="wave"
/>
</Box> </Box>
</Box> </Box>
))} ))}
+139 -139
View File
@@ -87,9 +87,7 @@ export function SealedSecretDetail() {
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
Failed to load SealedSecret Failed to load SealedSecret
</Typography> </Typography>
<Typography variant="body2"> <Typography variant="body2">{String(error)}</Typography>
{String(error)}
</Typography>
</Alert> </Alert>
</Box> </Box>
); );
@@ -162,165 +160,167 @@ export function SealedSecretDetail() {
<span>{sealedSecret.metadata.name}</span> <span>{sealedSecret.metadata.name}</span>
</Box> </Box>
<Box> <Box>
{permissions?.canUpdate && ( {permissions?.canUpdate && (
<Button <Button
variant="outlined" variant="outlined"
onClick={handleRotate} onClick={handleRotate}
disabled={rotating} disabled={rotating}
sx={{ mr: 1 }} sx={{ mr: 1 }}
> >
{rotating ? 'Re-encrypting...' : 'Re-encrypt'} {rotating ? 'Re-encrypting...' : 'Re-encrypt'}
</Button> </Button>
)} )}
{permissions?.canDelete && ( {permissions?.canDelete && (
<Button <Button
variant="outlined" variant="outlined"
color="error" color="error"
onClick={() => setDeleteDialogOpen(true)} onClick={() => setDeleteDialogOpen(true)}
> >
Delete Delete
</Button> </Button>
)} )}
</Box>
</Box> </Box>
</Box> }
} >
>
<NameValueTable
rows={[
{
name: 'Name',
value: String(sealedSecret.metadata.name || ''),
},
{
name: 'Namespace',
value: String(sealedSecret.metadata.namespace || ''),
},
{
name: 'Scope',
value: String(formatScope(sealedSecret.scope)),
},
{
name: 'Sync Status',
value: (
<StatusLabel status={sealedSecret.isSynced ? 'success' : 'error'}>
{sealedSecret.isSynced ? 'Synced' : 'Not Synced'}
</StatusLabel>
),
},
{
name: 'Status Message',
value: String(sealedSecret.syncMessage || 'Unknown'),
hide: !sealedSecret.syncCondition,
},
{
name: 'Age',
value: String(sealedSecret.getAge() || ''),
},
{
name: 'Created',
value: sealedSecret.metadata.creationTimestamp
? new Date(sealedSecret.metadata.creationTimestamp).toLocaleString()
: 'Unknown',
},
]}
/>
</SectionBox>
<SectionBox title="Encrypted Data">
<SimpleTable
data={encryptedKeys.map(key => ({
key,
value: sealedSecret.spec.encryptedData[key],
}))}
columns={[
{
label: 'Key',
getter: (row: any) => row.key,
},
{
label: 'Encrypted Value',
getter: (row: any) => {
const val = row.value;
return val.length > 40 ? val.substring(0, 40) + '...' : val;
},
},
{
label: 'Actions',
getter: (row: any) =>
canDecrypt ? (
<Button size="small" onClick={() => setDecryptKey(row.key)}>
Decrypt
</Button>
) : (
<Button size="small" disabled title="No permission to access Secrets">
Decrypt
</Button>
),
},
]}
/>
</SectionBox>
{sealedSecret.spec.template && (
<SectionBox title="Template">
<NameValueTable <NameValueTable
rows={[ rows={[
{ {
name: 'Secret Type', name: 'Name',
value: String(sealedSecret.spec.template.type || 'Opaque'), value: String(sealedSecret.metadata.name || ''),
}, },
{ {
name: 'Labels', name: 'Namespace',
value: String(JSON.stringify(sealedSecret.spec.template.metadata?.labels || {})), value: String(sealedSecret.metadata.namespace || ''),
hide: !sealedSecret.spec.template.metadata?.labels,
}, },
{ {
name: 'Annotations', name: 'Scope',
value: String( value: String(formatScope(sealedSecret.scope)),
JSON.stringify(sealedSecret.spec.template.metadata?.annotations || {}) },
{
name: 'Sync Status',
value: (
<StatusLabel status={sealedSecret.isSynced ? 'success' : 'error'}>
{sealedSecret.isSynced ? 'Synced' : 'Not Synced'}
</StatusLabel>
), ),
hide: !sealedSecret.spec.template.metadata?.annotations, },
{
name: 'Status Message',
value: String(sealedSecret.syncMessage || 'Unknown'),
hide: !sealedSecret.syncCondition,
},
{
name: 'Age',
value: String(sealedSecret.getAge() || ''),
},
{
name: 'Created',
value: sealedSecret.metadata.creationTimestamp
? new Date(sealedSecret.metadata.creationTimestamp).toLocaleString()
: 'Unknown',
}, },
]} ]}
/> />
</SectionBox> </SectionBox>
)}
<SectionBox title="Resulting Secret"> <SectionBox title="Encrypted Data">
{secret ? ( <SimpleTable
<NameValueTable data={encryptedKeys.map(key => ({
rows={[ key,
value: sealedSecret.spec.encryptedData[key],
}))}
columns={[
{ {
name: 'Status', label: 'Key',
value: <StatusLabel status="success">Secret exists</StatusLabel>, getter: (row: any) => row.key,
}, },
{ {
name: 'Keys', label: 'Encrypted Value',
value: String(Object.keys(secret.data || {}).join(', ') || 'None'), getter: (row: any) => {
const val = row.value;
return val.length > 40 ? val.substring(0, 40) + '...' : val;
},
}, },
{ {
name: 'Link', label: 'Actions',
value: ( getter: (row: any) =>
<Link canDecrypt ? (
routeName="secret" <Button size="small" onClick={() => setDecryptKey(row.key)}>
params={{ Decrypt
namespace: String(secret.metadata.namespace || ''), </Button>
name: String(secret.metadata.name || ''), ) : (
}} <Button size="small" disabled title="No permission to access Secrets">
> Decrypt
View Secret </Button>
</Link> ),
),
}, },
]} ]}
/> />
) : ( </SectionBox>
<Box p={2}>
<StatusLabel status="warning">Secret not yet created</StatusLabel> {sealedSecret.spec.template && (
<p>The controller will create the Secret once it processes this SealedSecret.</p> <SectionBox title="Template">
</Box> <NameValueTable
rows={[
{
name: 'Secret Type',
value: String(sealedSecret.spec.template.type || 'Opaque'),
},
{
name: 'Labels',
value: String(
JSON.stringify(sealedSecret.spec.template.metadata?.labels || {})
),
hide: !sealedSecret.spec.template.metadata?.labels,
},
{
name: 'Annotations',
value: String(
JSON.stringify(sealedSecret.spec.template.metadata?.annotations || {})
),
hide: !sealedSecret.spec.template.metadata?.annotations,
},
]}
/>
</SectionBox>
)} )}
</SectionBox>
<SectionBox title="Resulting Secret">
{secret ? (
<NameValueTable
rows={[
{
name: 'Status',
value: <StatusLabel status="success">Secret exists</StatusLabel>,
},
{
name: 'Keys',
value: String(Object.keys(secret.data || {}).join(', ') || 'None'),
},
{
name: 'Link',
value: (
<Link
routeName="secret"
params={{
namespace: String(secret.metadata.namespace || ''),
name: String(secret.metadata.name || ''),
}}
>
View Secret
</Link>
),
},
]}
/>
) : (
<Box p={2}>
<StatusLabel status="warning">Secret not yet created</StatusLabel>
<p>The controller will create the Secret once it processes this SealedSecret.</p>
</Box>
)}
</SectionBox>
</Box> </Box>
{decryptKey && ( {decryptKey && (
+7 -7
View File
@@ -126,9 +126,7 @@ export function SealedSecretList() {
// Show error if CRD is not installed // Show error if CRD is not installed
if (error) { if (error) {
return ( return (
<SectionBox <SectionBox title="Sealed Secrets">
title="Sealed Secrets"
>
<Box p={2}> <Box p={2}>
<StatusLabel status="error">Error</StatusLabel> <StatusLabel status="error">Error</StatusLabel>
<Box mt={2}> <Box mt={2}>
@@ -139,7 +137,11 @@ export function SealedSecretList() {
cluster. cluster.
</p> </p>
<p> <p>
Install with: <code>kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml</code> Install with:{' '}
<code>
kubectl apply -f
https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
</code>
</p> </p>
</> </>
) : ( ) : (
@@ -153,9 +155,7 @@ export function SealedSecretList() {
return ( return (
<> <>
<SectionBox <SectionBox title="Sealed Secrets">
title="Sealed Secrets"
>
<VersionWarning autoDetect showDetails={false} /> <VersionWarning autoDetect showDetails={false} />
<SectionFilterHeader title="" noNamespaceFilter={false} actions={actions} /> <SectionFilterHeader title="" noNamespaceFilter={false} actions={actions} />
<SimpleTable data={sealedSecrets} columns={columns} /> <SimpleTable data={sealedSecrets} columns={columns} />
+8 -2
View File
@@ -5,7 +5,11 @@
*/ */
import { K8s } from '@kinvolk/headlamp-plugin/lib'; import { K8s } from '@kinvolk/headlamp-plugin/lib';
import { SectionBox, SimpleTable, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import {
SectionBox,
SimpleTable,
StatusLabel,
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { Box, Button, Chip } from '@mui/material'; import { Box, Button, Chip } from '@mui/material';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import React from 'react'; import React from 'react';
@@ -27,7 +31,9 @@ interface SealingKey {
*/ */
export function SealingKeysView() { export function SealingKeysView() {
const config = getPluginConfig(); const config = getPluginConfig();
const [secrets, , loading] = K8s.ResourceClasses.Secret.useList({ namespace: config.controllerNamespace }); const [secrets, , loading] = K8s.ResourceClasses.Secret.useList({
namespace: config.controllerNamespace,
});
const { enqueueSnackbar } = useSnackbar(); const { enqueueSnackbar } = useSnackbar();
// Filter for sealing key secrets // Filter for sealing key secrets
+5 -1
View File
@@ -6,7 +6,11 @@
*/ */
import { Link } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import { Link } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { NameValueTable, SectionBox, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import {
NameValueTable,
SectionBox,
StatusLabel,
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import React from 'react'; import React from 'react';
import { SealedSecret } from '../lib/SealedSecretCRD'; import { SealedSecret } from '../lib/SealedSecretCRD';
+11 -9
View File
@@ -41,9 +41,7 @@ export function VersionWarning({ autoDetect = true, showDetails = false }: Versi
} else if (result.ok === false) { } else if (result.ok === false) {
setDetectedVersion(null); setDetectedVersion(null);
// Ensure error is always a string // Ensure error is always a string
const errorMessage = typeof result.error === 'string' const errorMessage = typeof result.error === 'string' ? result.error : String(result.error);
? result.error
: String(result.error);
setError(errorMessage); setError(errorMessage);
} }
} catch (e) { } catch (e) {
@@ -70,11 +68,14 @@ export function VersionWarning({ autoDetect = true, showDetails = false }: Versi
if (error) { if (error) {
return ( return (
<Box mb={2}> <Box mb={2}>
<Alert severity="error" action={ <Alert
<Button color="inherit" size="small" onClick={detectVersion}> severity="error"
Retry action={
</Button> <Button color="inherit" size="small" onClick={detectVersion}>
}> Retry
</Button>
}
>
<strong>API Version Detection Failed</strong> <strong>API Version Detection Failed</strong>
<br /> <br />
{String(error)} {String(error)}
@@ -84,7 +85,8 @@ export function VersionWarning({ autoDetect = true, showDetails = false }: Versi
<br /> <br />
Install Sealed Secrets with:{' '} Install Sealed Secrets with:{' '}
<code style={{ fontSize: '0.875em' }}> <code style={{ fontSize: '0.875em' }}>
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml kubectl apply -f
https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
</code> </code>
<br /> <br />
Or visit:{' '} Or visit:{' '}
+1 -8
View File
@@ -15,14 +15,7 @@ import {
parsePublicKeyFromCert, parsePublicKeyFromCert,
} from '../lib/crypto'; } from '../lib/crypto';
import { validateSecretKey, validateSecretName, validateSecretValue } from '../lib/validators'; import { validateSecretKey, validateSecretName, validateSecretValue } from '../lib/validators';
import { import { AsyncResult, CertificateInfo, Err, Ok, PlaintextValue, SealedSecretScope } from '../types';
AsyncResult,
CertificateInfo,
Err,
Ok,
PlaintextValue,
SealedSecretScope,
} from '../types';
/** /**
* Request parameters for encryption * Request parameters for encryption
+1 -1
View File
@@ -2,7 +2,7 @@
* SealedSecret Custom Resource Definition * SealedSecret Custom Resource Definition
*/ */
import { ApiProxy,K8s } from '@kinvolk/headlamp-plugin/lib'; import { ApiProxy, K8s } from '@kinvolk/headlamp-plugin/lib';
const { apiFactoryWithNamespace } = ApiProxy; const { apiFactoryWithNamespace } = ApiProxy;
const { KubeObject } = K8s.cluster; const { KubeObject } = K8s.cluster;
+8 -2
View File
@@ -39,7 +39,10 @@ describe('retry logic', () => {
.mockResolvedValueOnce({ ok: false, error: 'error2' }) .mockResolvedValueOnce({ ok: false, error: 'error2' })
.mockResolvedValueOnce({ ok: true, value: 'success' }); .mockResolvedValueOnce({ ok: true, value: 'success' });
const promise = retryWithBackoff(failTwiceThenSucceed, { maxAttempts: 3, initialDelayMs: 100 }); const promise = retryWithBackoff(failTwiceThenSucceed, {
maxAttempts: 3,
initialDelayMs: 100,
});
await vi.runAllTimersAsync(); await vi.runAllTimersAsync();
const result = await promise; const result = await promise;
@@ -72,7 +75,10 @@ describe('retry logic', () => {
.mockResolvedValueOnce({ ok: false, error: 'error2' }) .mockResolvedValueOnce({ ok: false, error: 'error2' })
.mockResolvedValueOnce({ ok: true, value: 'success' }); .mockResolvedValueOnce({ ok: true, value: 'success' });
const promise = retryWithBackoff(failTwiceThenSucceed, { maxAttempts: 3, initialDelayMs: 1000 }); const promise = retryWithBackoff(failTwiceThenSucceed, {
maxAttempts: 3,
initialDelayMs: 1000,
});
// Fast-forward through retries // Fast-forward through retries
await vi.runAllTimersAsync(); await vi.runAllTimersAsync();
+6 -8
View File
@@ -109,9 +109,7 @@ export async function retryWithBackoff<T, E>(
const isLastAttempt = attempt === opts.maxAttempts - 1; const isLastAttempt = attempt === opts.maxAttempts - 1;
if (isLastAttempt) { if (isLastAttempt) {
// No more retries, return final error // No more retries, return final error
return Err( return Err(`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`);
`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`
);
} }
// Wait before retrying // Wait before retrying
@@ -124,9 +122,7 @@ export async function retryWithBackoff<T, E>(
const isLastAttempt = attempt === opts.maxAttempts - 1; const isLastAttempt = attempt === opts.maxAttempts - 1;
if (isLastAttempt) { if (isLastAttempt) {
return Err( return Err(`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`);
`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`
);
} }
const delay = calculateDelay(attempt, opts); const delay = calculateDelay(attempt, opts);
@@ -171,10 +167,12 @@ export function isRetryableHttpError(error: Error): boolean {
} }
// Check for specific retryable status codes // Check for specific retryable status codes
return message.includes('429') || // Too Many Requests return (
message.includes('429') || // Too Many Requests
message.includes('408') || // Request Timeout message.includes('408') || // Request Timeout
message.includes('503') || // Service Unavailable message.includes('503') || // Service Unavailable
message.includes('504'); // Gateway Timeout message.includes('504')
); // Gateway Timeout
} }
/** /**
+1 -3
View File
@@ -16,9 +16,7 @@ type KubeObjectInterface = K8s.cluster.KubeObjectInterface;
* return Ok(a / b); * return Ok(a / b);
* } * }
*/ */
export type Result<T, E = Error> = export type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
| { ok: true; value: T }
| { ok: false; error: E };
/** /**
* Async result type for promises that can fail * Async result type for promises that can fail