fix: Resolve bcrypt architecture and duplicate resource creation issues

Backend fixes:
- Fix bcrypt exec format error for ARM64 architecture
- Add architecture detection for MinIO client download
- Install build tools and rebuild bcrypt from source
- Add postinstall script to automatically rebuild bcrypt
- Rebuild bcrypt after copying files to ensure correct binary

Frontend fixes:
- Handle duplicate resource creation in QuickStartWizard
- Add error handling for "already exists" scenarios
- Prevent multiple executions with isCreating flag
- Allow wizard to complete even if resources already exist

These changes fix:
1. Backend crash on ARM64 due to bcrypt binary mismatch
2. 500 errors when resources already exist in QuickStartWizard
3. Race conditions when clicking Complete button multiple times

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-24 09:17:22 +02:00
parent b4fc144cd3
commit 9cdd7b0050
3 changed files with 95 additions and 39 deletions
@@ -73,6 +73,7 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [completedSetup, setCompletedSetup] = useState<any>(null);
const [isCreating, setIsCreating] = useState(false);
const steps = [
t('quickWizard:steps.createBucket'),
@@ -125,57 +126,102 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
};
const handleComplete = async () => {
// Prevent multiple executions
if (isCreating) {
return;
}
setIsCreating(true);
setLoading(true);
setError('');
const results = {
bucket: setupData.bucketName,
user: setupData.userName,
password: setupData.userPassword,
policy: `${setupData.bucketName}-${setupData.policyType}-policy`,
};
let bucketCreated = false;
let userCreated = false;
let policyCreated = false;
try {
// Step 1: Create bucket
await api.post('/buckets', { bucketName: setupData.bucketName }, {
timeout: 30000, // 30 seconds timeout
});
// Step 1: Create bucket (skip if already exists)
try {
await api.post('/buckets', { bucketName: setupData.bucketName }, {
timeout: 30000, // 30 seconds timeout
});
bucketCreated = true;
} catch (err: any) {
if (err.response?.data?.error?.includes('already own it') ||
err.response?.data?.error?.includes('BucketAlreadyOwnedByYou')) {
// Bucket already exists, that's ok
bucketCreated = true;
} else {
throw err;
}
}
// Step 2: Create user
await userService.createUser({
accessKey: setupData.userName,
secretKey: setupData.userPassword,
});
// Step 2: Create user (skip if already exists)
try {
await userService.createUser({
accessKey: setupData.userName,
secretKey: setupData.userPassword,
});
userCreated = true;
} catch (err: any) {
if (err.response?.data?.error?.includes('already exists') ||
err.response?.data?.error?.includes('UserAlreadyExists')) {
// User already exists, that's ok
userCreated = true;
} else {
throw err;
}
}
// Step 3: Create policy
const policyName = `${setupData.bucketName}-${setupData.policyType}-policy`;
// Step 3: Create policy (skip if already exists)
const policyName = results.policy;
// Map policy types to template keys
const templateKey = setupData.policyType === 'readwrite'
? 'bucketFullAccess'
: setupData.policyType === 'readonly'
? 'bucketReadOnly'
: 'bucketWriteOnly';
const policyJson = policyService.generatePolicyFromTemplate(
templateKey,
setupData.bucketName
);
await policyService.createPolicy({
name: policyName,
policy: policyJson,
});
try {
// Map policy types to template keys
const templateKey = setupData.policyType === 'readwrite'
? 'bucketFullAccess'
: setupData.policyType === 'readonly'
? 'bucketReadOnly'
: 'bucketWriteOnly';
const policyJson = policyService.generatePolicyFromTemplate(
templateKey,
setupData.bucketName
);
await policyService.createPolicy({
name: policyName,
policy: policyJson,
});
policyCreated = true;
} catch (err: any) {
if (err.response?.data?.error?.includes('already exists') ||
err.response?.data?.error?.includes('PolicyAlreadyExists')) {
// Policy already exists, that's ok
policyCreated = true;
} else {
throw err;
}
}
// Step 4: Attach policy to user
// Step 4: Attach policy to user (always try this)
await policyService.attachPolicy({
policyName: policyName,
userName: setupData.userName,
});
setCompletedSetup({
bucket: setupData.bucketName,
user: setupData.userName,
password: setupData.userPassword,
policy: policyName,
});
setCompletedSetup(results);
setActiveStep(steps.length);
} catch (err) {
setError(err instanceof Error ? err.message : t('quickWizard:errors.creationFailed'));
setIsCreating(false);
} finally {
setLoading(false);
}
@@ -199,6 +245,7 @@ const QuickStartWizard: React.FC<QuickStartWizardProps> = ({
});
setError('');
setCompletedSetup(null);
setIsCreating(false);
}
};