fix: Resolve UI errors and improve user creation flow
- Fix validateDOMNesting warnings in Reports and QuickStartWizard components - Fix user creation API field mapping (accessKey/secretKey to username/password) - Increase timeout for bucket-with-user creation to prevent socket hang up errors - Improve credential display with clear Access Key/Secret Key labels - Add MinIO CLI connection examples to credential displays - Make timeout 60 seconds for complex operations that create bucket, user, and policy This fixes: - React DOM nesting warnings with Chip components - 400 Bad Request errors when creating users - Socket hang up errors during bucket+user creation - Clear display of MinIO access credentials 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -119,10 +119,13 @@ const CreateBucketDialog: React.FC<CreateBucketDialogProps> = ({
|
|||||||
|
|
||||||
if (data.createUser) {
|
if (data.createUser) {
|
||||||
// Create bucket with user (like the script)
|
// Create bucket with user (like the script)
|
||||||
|
// This operation can take longer as it creates bucket, user, and policy
|
||||||
await api.post('/buckets/with-user', {
|
await api.post('/buckets/with-user', {
|
||||||
bucketName: data.bucketName,
|
bucketName: data.bucketName,
|
||||||
username: data.username!,
|
username: data.username!,
|
||||||
password: data.password!,
|
password: data.password!,
|
||||||
|
}, {
|
||||||
|
timeout: 60000, // 60 seconds timeout for this complex operation
|
||||||
});
|
});
|
||||||
setCredentials({
|
setCredentials({
|
||||||
username: data.username!,
|
username: data.username!,
|
||||||
@@ -300,7 +303,7 @@ const CreateBucketDialog: React.FC<CreateBucketDialogProps> = ({
|
|||||||
{credentials && (
|
{credentials && (
|
||||||
<Box sx={{ mt: 2 }}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
User Credentials
|
MinIO User Credentials
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||||
Save these credentials securely. They won't be shown again.
|
Save these credentials securely. They won't be shown again.
|
||||||
@@ -309,27 +312,36 @@ const CreateBucketDialog: React.FC<CreateBucketDialogProps> = ({
|
|||||||
<Box sx={{ bgcolor: 'grey.100', p: 2, borderRadius: 1, mb: 2 }}>
|
<Box sx={{ bgcolor: 'grey.100', p: 2, borderRadius: 1, mb: 2 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Username:</strong> {credentials.username}
|
<strong>Access Key (Username):</strong> {credentials.username}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => copyToClipboard(credentials.username)}
|
onClick={() => copyToClipboard(credentials.username)}
|
||||||
|
title="Copy access key"
|
||||||
>
|
>
|
||||||
<ContentCopy fontSize="small" />
|
<ContentCopy fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Password:</strong> {credentials.password}
|
<strong>Secret Key (Password):</strong> {credentials.password}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => copyToClipboard(credentials.password)}
|
onClick={() => copyToClipboard(credentials.password)}
|
||||||
|
title="Copy secret key"
|
||||||
>
|
>
|
||||||
<ContentCopy fontSize="small" />
|
<ContentCopy fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Use these credentials to connect via MinIO Client:
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ bgcolor: 'grey.900', color: 'grey.100', p: 1, borderRadius: 1, fontFamily: 'monospace', fontSize: '0.875rem', mt: 1 }}>
|
||||||
|
mc alias set myminio http://your-minio-server:9000 {credentials.username} {credentials.password}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,9 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 1: Create bucket
|
// Step 1: Create bucket
|
||||||
await api.post('/buckets', { name: setupData.bucketName });
|
await api.post('/buckets', { name: setupData.bucketName }, {
|
||||||
|
timeout: 30000, // 30 seconds timeout
|
||||||
|
});
|
||||||
|
|
||||||
// Step 2: Create user
|
// Step 2: Create user
|
||||||
await userService.createUser({
|
await userService.createUser({
|
||||||
@@ -165,7 +167,7 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCopyCredentials = () => {
|
const handleCopyCredentials = () => {
|
||||||
const credentials = `MinIO Credentials\n\nBucket: ${completedSetup.bucket}\nUsername: ${completedSetup.user}\nPassword: ${completedSetup.password}\nPolicy: ${completedSetup.policy}`;
|
const credentials = `MinIO Credentials\n\nBucket: ${completedSetup.bucket}\nAccess Key: ${completedSetup.user}\nSecret Key: ${completedSetup.password}\nPolicy: ${completedSetup.policy}\n\nConnection Example:\nmc alias set myminio http://your-minio-server:9000 ${completedSetup.user} ${completedSetup.password}`;
|
||||||
navigator.clipboard.writeText(credentials);
|
navigator.clipboard.writeText(credentials);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -335,14 +337,16 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
|
|||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Access Level"
|
primary="Access Level"
|
||||||
secondary={
|
secondary={
|
||||||
<Chip
|
<Box component="span">
|
||||||
label={setupData.policyType}
|
<Chip
|
||||||
size="small"
|
label={setupData.policyType}
|
||||||
color={
|
size="small"
|
||||||
setupData.policyType === 'readwrite' ? 'success' :
|
color={
|
||||||
setupData.policyType === 'readonly' ? 'info' : 'warning'
|
setupData.policyType === 'readwrite' ? 'success' :
|
||||||
}
|
setupData.policyType === 'readonly' ? 'info' : 'warning'
|
||||||
/>
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
@@ -371,17 +375,17 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
|
|||||||
<Paper variant="outlined" sx={{ p: 3, mt: 2, bgcolor: 'grey.50' }}>
|
<Paper variant="outlined" sx={{ p: 3, mt: 2, bgcolor: 'grey.50' }}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
<Assignment sx={{ verticalAlign: 'middle', mr: 1 }} />
|
<Assignment sx={{ verticalAlign: 'middle', mr: 1 }} />
|
||||||
Your Credentials
|
Your MinIO Credentials
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ fontFamily: 'monospace', mt: 2 }}>
|
<Box sx={{ fontFamily: 'monospace', mt: 2 }}>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Bucket:</strong> {completedSetup.bucket}
|
<strong>Bucket:</strong> {completedSetup.bucket}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Username:</strong> {completedSetup.user}
|
<strong>Access Key:</strong> {completedSetup.user}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Password:</strong> {completedSetup.password}
|
<strong>Secret Key:</strong> {completedSetup.password}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
<strong>Policy:</strong> {completedSetup.policy}
|
<strong>Policy:</strong> {completedSetup.policy}
|
||||||
|
|||||||
@@ -311,14 +311,16 @@ const Reports: React.FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
{scheduleInfo && (
|
{scheduleInfo && (
|
||||||
<>
|
<>
|
||||||
<Typography variant="body2" paragraph>
|
<Box sx={{ mb: 2 }}>
|
||||||
<strong>Status:</strong>{' '}
|
<Typography variant="body2" component="span">
|
||||||
|
<strong>Status:</strong>{' '}
|
||||||
|
</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
label={scheduleInfo.enabled ? 'Enabled' : 'Disabled'}
|
label={scheduleInfo.enabled ? 'Enabled' : 'Disabled'}
|
||||||
size="small"
|
size="small"
|
||||||
color={scheduleInfo.enabled ? 'success' : 'default'}
|
color={scheduleInfo.enabled ? 'success' : 'default'}
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Box>
|
||||||
<Typography variant="body2" paragraph>
|
<Typography variant="body2" paragraph>
|
||||||
<strong>Schedule:</strong> {scheduleInfo.schedule}
|
<strong>Schedule:</strong> {scheduleInfo.schedule}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ class UserService {
|
|||||||
|
|
||||||
async createUser(userData: CreateUserRequest): Promise<void> {
|
async createUser(userData: CreateUserRequest): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await axios.post(`${this.baseURL}/users`, userData, {
|
await axios.post(`${this.baseURL}/users`, {
|
||||||
|
username: userData.accessKey,
|
||||||
|
password: userData.secretKey,
|
||||||
|
}, {
|
||||||
headers: getAuthHeaders(),
|
headers: getAuthHeaders(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user