349 lines
8.0 KiB
Markdown
349 lines
8.0 KiB
Markdown
# Mission Creation Flow - Why You Can Create Without N8N API Key
|
|
|
|
## 🔍 Current Behavior Explained
|
|
|
|
You're absolutely right! You **CAN** create missions without `N8N_API_KEY` because of how the code is structured.
|
|
|
|
---
|
|
|
|
## 📋 Current Flow Order
|
|
|
|
Looking at `app/api/missions/route.ts`, here's the **actual execution order**:
|
|
|
|
```
|
|
1. ✅ Create mission in database (line 260)
|
|
↓
|
|
2. ✅ Create mission users (line 298)
|
|
↓
|
|
3. ✅ Upload logo to Minio (line 318)
|
|
↓
|
|
4. ✅ Upload attachments to Minio (line 362)
|
|
↓
|
|
5. ✅ Verify files exist (line 391)
|
|
↓
|
|
6. ⚠️ Trigger N8N workflow (line 430)
|
|
↓
|
|
7. ❌ If N8N fails → Error thrown (line 437)
|
|
↓
|
|
8. ⚠️ Error caught → Cleanup files (line 458)
|
|
↓
|
|
9. ❌ Return 500 error BUT mission stays in database!
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 The Problem
|
|
|
|
### What Happens When N8N Fails
|
|
|
|
1. **Mission is created** in database (line 260) ✅
|
|
2. **Files are uploaded** to Minio ✅
|
|
3. **N8N is called** but fails (no API key, webhook not registered, etc.) ❌
|
|
4. **Error is thrown** (line 437) ❌
|
|
5. **Files are cleaned up** (line 458) ✅
|
|
6. **500 error is returned** to frontend ❌
|
|
7. **BUT: Mission remains in database!** ⚠️
|
|
|
|
### Result
|
|
|
|
- ✅ Mission exists in database
|
|
- ❌ No integration IDs saved (N8N never called `/mission-created`)
|
|
- ❌ Files deleted from Minio (cleanup)
|
|
- ❌ Frontend shows error
|
|
- ⚠️ **Orphaned mission in database**
|
|
|
|
---
|
|
|
|
## 🔍 Code Analysis
|
|
|
|
### Step 1: Mission Created (Line 260)
|
|
|
|
```typescript
|
|
const mission = await prisma.mission.create({
|
|
data: missionData
|
|
});
|
|
```
|
|
|
|
**This happens FIRST**, before N8N is even called.
|
|
|
|
### Step 2: N8N Called (Line 430)
|
|
|
|
```typescript
|
|
const workflowResult = await n8nService.triggerMissionCreation(n8nData);
|
|
|
|
if (!workflowResult.success) {
|
|
throw new Error(workflowResult.error || 'N8N workflow failed');
|
|
}
|
|
```
|
|
|
|
**If N8N fails**, an error is thrown.
|
|
|
|
### Step 3: Error Handling (Line 445-477)
|
|
|
|
```typescript
|
|
} catch (error) {
|
|
logger.error('Error in final verification or n8n', {
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
throw error; // Re-throws to outer catch
|
|
}
|
|
|
|
// Outer catch (line 451)
|
|
} catch (error) {
|
|
// Cleanup files
|
|
for (const file of uploadedFiles) {
|
|
await s3Client.send(new DeleteObjectCommand({...}));
|
|
}
|
|
|
|
return NextResponse.json({
|
|
error: 'Failed to create mission',
|
|
details: error instanceof Error ? error.message : String(error)
|
|
}, { status: 500 });
|
|
}
|
|
```
|
|
|
|
**Notice**: The outer catch block:
|
|
- ✅ Cleans up files from Minio
|
|
- ❌ **Does NOT delete the mission from database**
|
|
- ❌ Returns 500 error
|
|
|
|
---
|
|
|
|
## ⚠️ Why This Is a Problem
|
|
|
|
### Scenario: N8N Fails (No API Key)
|
|
|
|
1. User creates mission
|
|
2. Mission saved to database ✅
|
|
3. Files uploaded to Minio ✅
|
|
4. N8N called → Fails (no API key) ❌
|
|
5. Error thrown
|
|
6. Files cleaned up ✅
|
|
7. **Mission still in database** ⚠️
|
|
8. Frontend shows error
|
|
9. User sees error but mission exists
|
|
10. **Mission has no integration IDs** (N8N never saved them)
|
|
|
|
### Result
|
|
|
|
- **Orphaned missions** in database without integration IDs
|
|
- **Inconsistent state**: Mission exists but integrations don't
|
|
- **Deletion won't work**: No IDs to send to N8N deletion workflow
|
|
|
|
---
|
|
|
|
## ✅ Solutions
|
|
|
|
### Solution 1: Make N8N Optional (Current Behavior - But Better Error Handling)
|
|
|
|
**Keep current flow but improve error handling**:
|
|
|
|
```typescript
|
|
// After N8N call
|
|
if (!workflowResult.success) {
|
|
logger.warn('N8N workflow failed, but mission created', {
|
|
error: workflowResult.error,
|
|
missionId: mission.id
|
|
});
|
|
// Don't throw error - mission is created, N8N is optional
|
|
// Return success but with warning
|
|
return NextResponse.json({
|
|
success: true,
|
|
mission,
|
|
warning: 'Mission created but integrations may not be set up',
|
|
n8nError: workflowResult.error
|
|
});
|
|
}
|
|
```
|
|
|
|
**Pros**:
|
|
- Mission creation succeeds even if N8N fails
|
|
- User gets feedback about partial success
|
|
|
|
**Cons**:
|
|
- Mission exists without integration IDs
|
|
- Deletion won't work properly
|
|
|
|
### Solution 2: Delete Mission If N8N Fails (Strict)
|
|
|
|
**Delete mission if N8N fails**:
|
|
|
|
```typescript
|
|
} catch (error) {
|
|
logger.error('Error in final verification or n8n', {
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
|
|
// Delete mission if N8N fails
|
|
try {
|
|
await prisma.mission.delete({
|
|
where: { id: mission.id }
|
|
});
|
|
logger.debug('Mission deleted due to N8N failure', { missionId: mission.id });
|
|
} catch (deleteError) {
|
|
logger.error('Failed to delete mission after N8N failure', {
|
|
missionId: mission.id,
|
|
error: deleteError
|
|
});
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
```
|
|
|
|
**Pros**:
|
|
- No orphaned missions
|
|
- Consistent state
|
|
|
|
**Cons**:
|
|
- Mission creation fails completely if N8N is down
|
|
- User loses all work if N8N has issues
|
|
|
|
### Solution 3: Make N8N Non-Blocking (Recommended)
|
|
|
|
**Don't throw error if N8N fails, just log it**:
|
|
|
|
```typescript
|
|
const workflowResult = await n8nService.triggerMissionCreation(n8nData);
|
|
|
|
if (!workflowResult.success) {
|
|
logger.warn('N8N workflow failed, but continuing', {
|
|
error: workflowResult.error,
|
|
missionId: mission.id
|
|
});
|
|
// Don't throw - mission is created, N8N can be retried later
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
mission,
|
|
message: workflowResult.success
|
|
? 'Mission created successfully with all integrations'
|
|
: 'Mission created but integrations may need to be set up manually'
|
|
});
|
|
```
|
|
|
|
**Pros**:
|
|
- Mission creation succeeds
|
|
- User gets clear feedback
|
|
- Can retry N8N later
|
|
|
|
**Cons**:
|
|
- Mission may exist without integration IDs
|
|
- Need manual retry mechanism
|
|
|
|
### Solution 4: Transaction-Based (Best But Complex)
|
|
|
|
**Use database transaction and rollback if N8N fails**:
|
|
|
|
```typescript
|
|
const result = await prisma.$transaction(async (tx) => {
|
|
// Create mission
|
|
const mission = await tx.mission.create({...});
|
|
|
|
// Upload files
|
|
// ...
|
|
|
|
// Try N8N
|
|
const workflowResult = await n8nService.triggerMissionCreation(n8nData);
|
|
|
|
if (!workflowResult.success) {
|
|
throw new Error('N8N workflow failed');
|
|
}
|
|
|
|
return mission;
|
|
});
|
|
```
|
|
|
|
**Pros**:
|
|
- Atomic operation
|
|
- No orphaned missions
|
|
|
|
**Cons**:
|
|
- Complex to implement
|
|
- Files already uploaded (can't rollback Minio in transaction)
|
|
|
|
---
|
|
|
|
## 🎯 Recommended Approach
|
|
|
|
**Hybrid Solution**: Make N8N non-blocking but add retry mechanism
|
|
|
|
```typescript
|
|
// After N8N call
|
|
if (!workflowResult.success) {
|
|
logger.warn('N8N workflow failed, mission created without integrations', {
|
|
error: workflowResult.error,
|
|
missionId: mission.id
|
|
});
|
|
|
|
// Mission is created, but mark it for retry
|
|
await prisma.mission.update({
|
|
where: { id: mission.id },
|
|
data: {
|
|
// Add a flag to indicate N8N needs retry
|
|
// Or just log it and handle manually
|
|
}
|
|
});
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
mission,
|
|
message: workflowResult.success
|
|
? 'Mission created successfully with all integrations'
|
|
: 'Mission created. Integrations will be set up shortly.'
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Current vs Recommended
|
|
|
|
### Current Behavior
|
|
- ✅ Mission created even if N8N fails
|
|
- ❌ No integration IDs saved
|
|
- ❌ Deletion won't work
|
|
- ❌ Orphaned missions
|
|
|
|
### Recommended Behavior
|
|
- ✅ Mission created even if N8N fails
|
|
- ⚠️ Integration IDs may be missing (but can be retried)
|
|
- ✅ User gets clear feedback
|
|
- ✅ Can retry N8N later
|
|
|
|
---
|
|
|
|
## 🔧 Quick Fix
|
|
|
|
If you want to keep current behavior but improve it:
|
|
|
|
**Change line 436-438** from:
|
|
```typescript
|
|
if (!workflowResult.success) {
|
|
throw new Error(workflowResult.error || 'N8N workflow failed');
|
|
}
|
|
```
|
|
|
|
**To**:
|
|
```typescript
|
|
if (!workflowResult.success) {
|
|
logger.warn('N8N workflow failed, but mission created', {
|
|
error: workflowResult.error,
|
|
missionId: mission.id
|
|
});
|
|
// Continue - mission is created, N8N can be retried
|
|
}
|
|
```
|
|
|
|
This way:
|
|
- ✅ Mission creation succeeds
|
|
- ⚠️ User gets warning about integrations
|
|
- ✅ Can manually trigger N8N later or add retry mechanism
|
|
|
|
---
|
|
|
|
**Document Created**: $(date)
|
|
**Issue**: Mission creation succeeds even when N8N fails, leading to orphaned missions without integration IDs
|
|
|