NeahNew/MISSION_DELETION_N8N_IDS_ISSUE_ANALYSIS.md
2026-01-09 11:19:32 +01:00

16 KiB

Mission Deletion N8N IDs Issue - Complete Analysis

🔍 Problem Statement

When deleting a mission, the N8N deletion workflow is not working because the database does not contain the integration IDs (Leantime, Outline, Gitea, RocketChat). This prevents N8N from properly cleaning up external integrations.


🔄 Current Flow Analysis

Mission Creation Flow

1. Frontend → POST /api/missions
   ↓
2. Backend creates mission in Prisma
   ✅ Mission created with NULL integration IDs:
      - leantimeProjectId: null
      - outlineCollectionId: null
      - giteaRepositoryUrl: null
      - rocketChatChannelId: null
   ↓
3. Backend uploads files to Minio
   ↓
4. Backend triggers N8N workflow (async)
   ✅ Sends missionId to N8N
   ↓
5. N8N creates external integrations:
   - Gitea repository
   - Leantime project
   - Outline collection
   - RocketChat channel
   ↓
6. N8N should call → POST /api/missions/mission-created
   ⚠️ PROBLEM: This callback may fail or not be called
   ↓
7. Backend should save IDs to database
   ❌ If step 6 fails, IDs are never saved

Mission Deletion Flow

1. Frontend → DELETE /api/missions/[missionId]
   ↓
2. Backend reads mission from database
   ❌ Integration IDs are NULL (if step 6 above failed)
   ↓
3. Backend prepares deletion data for N8N:
   {
     repoName: "",                    // Empty because giteaRepositoryUrl is null
     leantimeProjectId: 0,           // 0 because leantimeProjectId is null
     documentationCollectionId: "",   // Empty because outlineCollectionId is null
     rocketchatChannelId: ""         // Empty because rocketChatChannelId is null
   }
   ↓
4. Backend sends to N8N deletion workflow
   ❌ N8N receives empty IDs and cannot delete integrations
   ↓
5. N8N fails to clean up external resources

📋 Code Analysis

1. Mission Creation - Saving IDs

File: app/api/missions/route.ts

Lines 260-262: Mission is created WITHOUT integration IDs

const mission = await prisma.mission.create({
  data: missionData  // No integration IDs here
});

Lines 413-423: N8N is triggered with missionId

const n8nData = {
  ...body,
  missionId: mission.id, // ✅ missionId is sent to N8N
  creatorId: userId,
  logoPath: logoPath,
  logoUrl: logoUrl,
  config: {
    N8N_API_KEY: process.env.N8N_API_KEY,
    MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL
  }
};

const workflowResult = await n8nService.triggerMissionCreation(n8nData);

Issue: The API returns success immediately without waiting for N8N to save the IDs.

2. N8N Callback Endpoint

File: app/api/missions/mission-created/route.ts

Lines 64-69: Endpoint prefers missionId over name + creatorId

if (body.missionId) {
  // ✅ Use missionId if provided (more reliable)
  logger.debug('Looking up mission by ID', { missionId: body.missionId });
  mission = await prisma.mission.findUnique({
    where: { id: body.missionId }
  });
}

Lines 128-150: Maps N8N fields to Prisma fields

// Mapper les champs N8N vers notre schéma Prisma
if (body.gitRepoUrl !== undefined) {
  updateData.giteaRepositoryUrl = body.gitRepoUrl || null;
}

if (body.leantimeProjectId !== undefined) {
  updateData.leantimeProjectId = body.leantimeProjectId 
    ? String(body.leantimeProjectId) 
    : null;
}

if (body.documentationCollectionId !== undefined) {
  updateData.outlineCollectionId = body.documentationCollectionId || null;
}

if (body.rocketchatChannelId !== undefined) {
  updateData.rocketChatChannelId = body.rocketchatChannelId || null;
}

Status: Endpoint exists and should work correctly

3. Mission Deletion - Reading IDs

File: app/api/missions/[missionId]/route.ts

Lines 302-311: Mission is fetched from database

const mission = await prisma.mission.findUnique({
  where: { id: params.missionId },
  include: {
    missionUsers: {
      include: {
        user: true
      }
    }
  }
});

Lines 356-372: Deletion data is prepared

const n8nDeletionData = {
  missionId: mission.id,
  name: mission.name,
  repoName: repoName,  // Extracted from giteaRepositoryUrl (may be empty)
  leantimeProjectId: mission.leantimeProjectId || 0,  // ❌ 0 if null
  documentationCollectionId: mission.outlineCollectionId || '',  // ❌ Empty if null
  rocketchatChannelId: mission.rocketChatChannelId || '',  // ❌ Empty if null
  // ...
};

Problem: If IDs are null in database, N8N receives empty values.


🔍 Root Cause Analysis

Possible Causes

  1. N8N Workflow Not Calling /mission-created

    • N8N workflow might not be configured to call the callback endpoint
    • The "Save Mission To API" node might be missing or misconfigured
    • Network issues preventing the callback
  2. N8N Callback Failing

    • API key mismatch
    • Mission lookup failing (name/creatorId mismatch)
    • Network timeout
    • Server error
  3. Timing Issues

    • N8N workflow takes time to complete
    • User deletes mission before N8N saves IDs
    • Race condition
  4. N8N Not Sending missionId

    • N8N might only send name + creatorId
    • If mission name is not unique, lookup fails

Verification Steps

Step 1: Check if IDs are being saved

Query the database:

SELECT 
  id, 
  name, 
  giteaRepositoryUrl, 
  leantimeProjectId, 
  outlineCollectionId, 
  rocketChatChannelId,
  createdAt
FROM "Mission"
WHERE createdAt > NOW() - INTERVAL '7 days'
ORDER BY createdAt DESC;

Expected: Recent missions should have integration IDs populated.

If NULL: N8N callback is not working.

Step 2: Check N8N Workflow Configuration

Verify N8N workflow has "Save Mission To API" node:

  • Node should POST to: {{ MISSION_API_URL }}/mission-created
  • Should include missionId in body
  • Should include x-api-key header
  • Should include integration IDs in body

Expected format:

{
  "missionId": "uuid-here",
  "name": "Mission Name",
  "creatorId": "user-id",
  "gitRepoUrl": "https://gite.slm-lab.net/alma/repo-name",
  "leantimeProjectId": "123",
  "documentationCollectionId": "collection-id",
  "rocketchatChannelId": "channel-id"
}

Step 3: Check Server Logs

Look for:

Mission Created Webhook Received
Received mission-created data: { ... }
Found mission: { id: "...", name: "..." }
Updating giteaRepositoryUrl: ...
Mission updated successfully

If missing: N8N is not calling the endpoint.

Step 4: Test N8N Callback Manually

Send test request:

curl -X POST https://hub.slm-lab.net/api/missions/mission-created \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_N8N_API_KEY" \
  -d '{
    "missionId": "existing-mission-id",
    "gitRepoUrl": "https://gite.slm-lab.net/alma/test-repo",
    "leantimeProjectId": "999",
    "documentationCollectionId": "test-collection",
    "rocketchatChannelId": "test-channel"
  }'

Expected: 200 OK with updated mission data.


🔧 Solutions

Solution 1: Verify N8N Workflow Configuration (IMMEDIATE)

Check N8N workflow "Save Mission To API" node:

  1. URL should be:

    {{ $node['Process Mission Data'].json.config.MISSION_API_URL }}/mission-created
    

    Or hardcoded:

    https://hub.slm-lab.net/api/missions/mission-created
    
  2. Headers should include:

    Content-Type: application/json
    x-api-key: {{ $node['Process Mission Data'].json.config.N8N_API_KEY }}
    
  3. Body should include:

    • missionId (from original request)
    • gitRepoUrl (from Git repository creation)
    • leantimeProjectId (from Leantime project creation)
    • documentationCollectionId (from Outline collection creation)
    • rocketchatChannelId (from RocketChat channel creation)
  4. Verify node execution:

    • Check N8N execution logs
    • Verify node is not set to "continueOnFail"
    • Check for errors in node execution

Solution 2: Add Logging to Track Callback (DEBUGGING)

Add more detailed logging in app/api/missions/mission-created/route.ts:

logger.debug('Mission Created Webhook Received', {
  headers: {
    hasApiKey: !!request.headers.get('x-api-key'),
    contentType: request.headers.get('content-type')
  }
});

const body = await request.json();
logger.debug('Received mission-created data', {
  missionId: body.missionId,
  name: body.name,
  creatorId: body.creatorId,
  hasGitRepoUrl: !!body.gitRepoUrl,
  hasLeantimeProjectId: !!body.leantimeProjectId,
  hasDocumentationCollectionId: !!body.documentationCollectionId,
  hasRocketchatChannelId: !!body.rocketchatChannelId,
  fullBody: body  // Log full body for debugging
});

Solution 3: Add Fallback Lookup (ROBUSTNESS)

Improve mission lookup in app/api/missions/mission-created/route.ts:

// Try missionId first
if (body.missionId) {
  mission = await prisma.mission.findUnique({
    where: { id: body.missionId }
  });
  
  if (!mission) {
    logger.warn('Mission not found by ID, trying name + creatorId', {
      missionId: body.missionId
    });
  }
}

// Fallback to name + creatorId
if (!mission && body.name && body.creatorId) {
  mission = await prisma.mission.findFirst({
    where: {
      name: body.name,
      creatorId: body.creatorId
    },
    orderBy: { createdAt: 'desc' }
  });
}

// If still not found, try just by name (last resort)
if (!mission && body.name) {
  logger.warn('Mission not found by name + creatorId, trying just name', {
    name: body.name,
    creatorId: body.creatorId
  });
  mission = await prisma.mission.findFirst({
    where: { name: body.name },
    orderBy: { createdAt: 'desc' }
  });
}

Solution 4: Add Retry Mechanism (RELIABILITY)

Add retry logic for N8N callback (if N8N supports it):

  • Configure N8N to retry failed callbacks
  • Or implement a webhook retry queue

Solution 5: Manual ID Update Script (MIGRATION)

Create a script to manually update existing missions:

// scripts/update-mission-ids.ts
import { prisma } from '@/lib/prisma';

async function updateMissionIds() {
  // Get missions without IDs
  const missions = await prisma.mission.findMany({
    where: {
      OR: [
        { giteaRepositoryUrl: null },
        { leantimeProjectId: null },
        { outlineCollectionId: null },
        { rocketChatChannelId: null }
      ]
    }
  });

  for (const mission of missions) {
    // Manually update IDs if you know them
    // Or query external services to find them
    await prisma.mission.update({
      where: { id: mission.id },
      data: {
        giteaRepositoryUrl: '...',  // From Gitea
        leantimeProjectId: '...',   // From Leantime
        outlineCollectionId: '...', // From Outline
        rocketChatChannelId: '...'   // From RocketChat
      }
    });
  }
}

🧪 Testing Plan

Test 1: Create Mission and Verify IDs Saved

  1. Create a new mission via frontend
  2. Wait 30-60 seconds for N8N to complete
  3. Query database to verify IDs are saved:
    SELECT * FROM "Mission" WHERE name = 'Test Mission';
    
  4. Expected: All integration IDs should be populated

Test 2: Check N8N Execution Logs

  1. Go to N8N execution history
  2. Find the latest mission creation execution
  3. Check "Save Mission To API" node:
    • Node executed successfully
    • Response is 200 OK
    • Body contains integration IDs

Test 3: Test Deletion with IDs

  1. Delete a mission that has IDs saved
  2. Check N8N deletion workflow execution
  3. Expected: N8N should receive non-empty IDs and successfully delete integrations

Test 4: Test Deletion without IDs

  1. Delete a mission that has NULL IDs
  2. Check N8N deletion workflow execution
  3. Expected: N8N receives empty IDs and logs warning (but mission still deleted)

📊 Expected vs Actual Behavior

Expected Behavior

Mission Creation:

  1. Mission created in database
  2. N8N workflow triggered
  3. N8N creates integrations
  4. N8N calls /mission-created with IDs
  5. IDs saved to database

Mission Deletion:

  1. Mission fetched from database (with IDs)
  2. IDs sent to N8N deletion workflow
  3. N8N deletes integrations
  4. Mission deleted from database

Actual Behavior (Current Issue)

Mission Creation:

  1. Mission created in database
  2. N8N workflow triggered
  3. N8N creates integrations
  4. N8N calls /mission-created (May fail)
  5. IDs saved to database (If step 4 fails)

Mission Deletion:

  1. Mission fetched from database (IDs are NULL)
  2. Empty IDs sent to N8N deletion workflow
  3. N8N cannot delete integrations
  4. Mission deleted from database (But integrations remain)

🎯 Immediate Action Items

  1. Verify N8N Workflow Configuration

    • Check "Save Mission To API" node exists
    • Verify URL, headers, and body format
    • Check execution logs for errors
  2. Check Server Logs

    • Look for /mission-created endpoint calls
    • Check for errors or missing API key
    • Verify mission lookup is working
  3. Test Manually

    • Create a test mission
    • Wait for N8N to complete
    • Check database for IDs
    • If missing, manually test the callback endpoint
  4. Fix N8N Workflow (if needed)

    • Ensure missionId is included in callback
    • Verify all integration IDs are included
    • Test the workflow end-to-end
  5. Update Existing Missions (if needed)

    • Manually update IDs for critical missions
    • Or create migration script

📝 Code Changes Needed

No Code Changes Required (if N8N is configured correctly)

The endpoint /api/missions/mission-created already exists and should work. The issue is likely:

  • N8N workflow not calling it
  • N8N workflow calling it incorrectly
  • Network/authentication issues

Optional Improvements

  1. Better error handling in /mission-created endpoint
  2. Retry mechanism for failed callbacks
  3. Monitoring/alerting when IDs are not saved
  4. Migration script for existing missions

🔍 Debugging Commands

Check Recent Missions Without IDs

SELECT 
  id, 
  name, 
  createdAt,
  CASE WHEN giteaRepositoryUrl IS NULL THEN 'MISSING' ELSE 'OK' END as gitea,
  CASE WHEN leantimeProjectId IS NULL THEN 'MISSING' ELSE 'OK' END as leantime,
  CASE WHEN outlineCollectionId IS NULL THEN 'MISSING' ELSE 'OK' END as outline,
  CASE WHEN rocketChatChannelId IS NULL THEN 'MISSING' ELSE 'OK' END as rocketchat
FROM "Mission"
WHERE createdAt > NOW() - INTERVAL '7 days'
ORDER BY createdAt DESC;

Check Mission with IDs

SELECT 
  id,
  name,
  giteaRepositoryUrl,
  leantimeProjectId,
  outlineCollectionId,
  rocketChatChannelId
FROM "Mission"
WHERE id = 'your-mission-id';

Test Callback Endpoint

# Replace with actual values
curl -X POST https://hub.slm-lab.net/api/missions/mission-created \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_N8N_API_KEY" \
  -d '{
    "missionId": "mission-uuid-here",
    "gitRepoUrl": "https://gite.slm-lab.net/alma/test",
    "leantimeProjectId": "123",
    "documentationCollectionId": "collection-456",
    "rocketchatChannelId": "channel-789"
  }'

Conclusion

Root Cause: N8N workflow is likely not calling /api/missions/mission-created endpoint, or the callback is failing silently.

Solution:

  1. Verify N8N workflow configuration
  2. Check N8N execution logs
  3. Test callback endpoint manually
  4. Fix N8N workflow if needed
  5. Manually update existing missions if necessary

Status: Endpoint exists and should work. Issue is in N8N workflow configuration or execution.


Document Created: $(date) Last Updated: $(date) Priority: HIGH - Blocks proper mission deletion