missions finition
This commit is contained in:
parent
1638dfec50
commit
680abdbc54
@ -201,7 +201,15 @@ export async function DELETE(
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Call N8N deletion webhook (non-blocking)
|
||||
// Step 3: Call N8N deletion webhook
|
||||
logger.debug('Preparing N8N deletion webhook call', {
|
||||
missionId: mission.id,
|
||||
hasGiteaUrl: !!mission.giteaRepositoryUrl,
|
||||
hasLeantimeId: !!mission.leantimeProjectId,
|
||||
hasOutlineId: !!mission.outlineCollectionId,
|
||||
hasRocketChatId: !!mission.rocketChatChannelId
|
||||
});
|
||||
|
||||
const n8nService = new N8nService();
|
||||
const n8nData = {
|
||||
missionId: mission.id,
|
||||
@ -216,13 +224,34 @@ export async function DELETE(
|
||||
penpotProjectId: mission.penpotProjectId
|
||||
};
|
||||
|
||||
// Call N8N but don't fail if it errors
|
||||
n8nService.triggerMissionDeletion(n8nData).catch(n8nError => {
|
||||
logger.error('N8N deletion webhook failed (mission still deleted from database)', {
|
||||
error: n8nError instanceof Error ? n8nError.message : String(n8nError),
|
||||
logger.debug('Calling N8N deletion webhook', {
|
||||
webhookUrl: process.env.N8N_DELETE_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook/mission-delete',
|
||||
hasApiKey: !!process.env.N8N_API_KEY,
|
||||
dataKeys: Object.keys(n8nData)
|
||||
});
|
||||
|
||||
// Call N8N but don't fail if it errors (mission deletion should still succeed)
|
||||
try {
|
||||
const n8nResult = await n8nService.triggerMissionDeletion(n8nData);
|
||||
logger.debug('N8N deletion webhook result', {
|
||||
success: n8nResult.success,
|
||||
hasError: !!n8nResult.error,
|
||||
missionId
|
||||
});
|
||||
});
|
||||
|
||||
if (!n8nResult.success) {
|
||||
logger.warn('N8N deletion webhook returned error (mission still deleted)', {
|
||||
error: n8nResult.error,
|
||||
missionId
|
||||
});
|
||||
}
|
||||
} catch (n8nError) {
|
||||
logger.error('N8N deletion webhook failed (mission still deleted from database)', {
|
||||
error: n8nError instanceof Error ? n8nError.message : String(n8nError),
|
||||
errorType: n8nError instanceof Error ? n8nError.name : typeof n8nError,
|
||||
missionId
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4: Delete from database (cascade will handle related records)
|
||||
await prisma.mission.delete({
|
||||
@ -236,13 +265,23 @@ export async function DELETE(
|
||||
message: 'Mission deleted successfully'
|
||||
});
|
||||
} catch (error: any) {
|
||||
const { missionId: errorMissionId } = await params;
|
||||
// Get missionId from params safely
|
||||
let errorMissionId = 'unknown';
|
||||
try {
|
||||
const paramsResult = await params;
|
||||
errorMissionId = paramsResult.missionId;
|
||||
} catch (paramsError) {
|
||||
// If we can't get params, use unknown
|
||||
logger.warn('Could not get missionId from params in error handler');
|
||||
}
|
||||
|
||||
logger.error('Error deleting mission', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
errorStack: error instanceof Error ? error.stack : undefined,
|
||||
missionId: errorMissionId
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to delete mission', details: error.message },
|
||||
{ error: 'Failed to delete mission', details: error instanceof Error ? error.message : String(error) },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
@ -496,16 +496,23 @@ export async function POST(request: Request) {
|
||||
MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL
|
||||
}
|
||||
};
|
||||
|
||||
// Log detailed information for debugging
|
||||
logger.debug('Sending to N8N', {
|
||||
missionId: n8nData.missionId,
|
||||
name: n8nData.name,
|
||||
hasLogo: !!n8nData.logoPath
|
||||
hasLogo: !!n8nData.logoPath,
|
||||
webhookUrl: process.env.N8N_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook/mission-created',
|
||||
hasApiKey: !!process.env.N8N_API_KEY,
|
||||
apiKeyLength: process.env.N8N_API_KEY ? process.env.N8N_API_KEY.length : 0,
|
||||
missionApiUrl: process.env.NEXT_PUBLIC_API_URL
|
||||
});
|
||||
|
||||
const workflowResult = await n8nService.triggerMissionCreation(n8nData);
|
||||
logger.debug('N8N workflow result', {
|
||||
success: workflowResult.success,
|
||||
hasError: !!workflowResult.error
|
||||
hasError: !!workflowResult.error,
|
||||
error: workflowResult.error
|
||||
});
|
||||
|
||||
if (!workflowResult.success) {
|
||||
|
||||
@ -161,6 +161,7 @@ services:
|
||||
# N8N Integration (required for mission creation workflow)
|
||||
N8N_API_KEY: ${N8N_API_KEY}
|
||||
N8N_WEBHOOK_URL: ${N8N_WEBHOOK_URL:-https://brain.slm-lab.net/webhook/mission-created}
|
||||
N8N_DELETE_WEBHOOK_URL: ${N8N_DELETE_WEBHOOK_URL:-https://brain.slm-lab.net/webhook/mission-delete}
|
||||
|
||||
# Autres variables d'environnement (ajoutez les vôtres)
|
||||
# volumes:
|
||||
|
||||
@ -63,6 +63,7 @@ NEXT_PUBLIC_APP_URL=https://hub.slm-lab.net
|
||||
# N8N est utilisé pour créer automatiquement les projets Leantime, repos Git, etc.
|
||||
N8N_API_KEY=VOTRE_N8N_API_KEY
|
||||
N8N_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-created
|
||||
N8N_DELETE_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-delete
|
||||
|
||||
# ============================================
|
||||
# Autres services (ajoutez selon vos besoins)
|
||||
|
||||
@ -13,6 +13,12 @@ export class N8nService {
|
||||
this.rollbackWebhookUrl = process.env.N8N_ROLLBACK_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook/mission-rollback';
|
||||
this.apiKey = process.env.N8N_API_KEY || '';
|
||||
|
||||
logger.debug('N8nService initialized', {
|
||||
webhookUrl: this.webhookUrl,
|
||||
hasApiKey: !!this.apiKey,
|
||||
apiKeyLength: this.apiKey ? this.apiKey.length : 0
|
||||
});
|
||||
|
||||
if (!this.apiKey) {
|
||||
logger.error('N8N_API_KEY is not set in environment variables');
|
||||
}
|
||||
@ -25,22 +31,45 @@ export class N8nService {
|
||||
logger.debug('Triggering n8n mission deletion workflow', {
|
||||
missionId: data.missionId,
|
||||
name: data.name,
|
||||
hasRepoName: !!data.repoName
|
||||
hasRepoName: !!data.repoName,
|
||||
repoName: data.repoName
|
||||
});
|
||||
logger.debug('Using deletion webhook URL', { url: deleteWebhookUrl });
|
||||
logger.debug('API key present', { present: !!this.apiKey });
|
||||
|
||||
const response = await fetchWithTimeout(deleteWebhookUrl, {
|
||||
method: 'POST',
|
||||
timeout: 30000, // 30 seconds
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': this.apiKey
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
logger.debug('API key present', {
|
||||
present: !!this.apiKey,
|
||||
length: this.apiKey ? this.apiKey.length : 0
|
||||
});
|
||||
logger.debug('Request payload', {
|
||||
keys: Object.keys(data),
|
||||
payloadSize: JSON.stringify(data).length
|
||||
});
|
||||
|
||||
logger.debug('Deletion webhook response', { status: response.status });
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetchWithTimeout(deleteWebhookUrl, {
|
||||
method: 'POST',
|
||||
timeout: 30000, // 30 seconds
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': this.apiKey
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
logger.debug('Deletion webhook response received', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Object.fromEntries(response.headers.entries())
|
||||
});
|
||||
} catch (fetchError) {
|
||||
logger.error('Failed to send deletion webhook request', {
|
||||
error: fetchError instanceof Error ? fetchError.message : String(fetchError),
|
||||
errorType: fetchError instanceof Error ? fetchError.name : typeof fetchError,
|
||||
url: deleteWebhookUrl,
|
||||
stack: fetchError instanceof Error ? fetchError.stack : undefined
|
||||
});
|
||||
throw fetchError;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
@ -157,19 +186,40 @@ export class N8nService {
|
||||
});
|
||||
|
||||
logger.debug('Using webhook URL', { url: this.webhookUrl });
|
||||
logger.debug('API key present', { present: !!this.apiKey });
|
||||
|
||||
const response = await fetchWithTimeout(this.webhookUrl, {
|
||||
method: 'POST',
|
||||
timeout: 30000, // 30 seconds
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': this.apiKey
|
||||
},
|
||||
body: JSON.stringify(cleanData),
|
||||
logger.debug('API key present', { present: !!this.apiKey, length: this.apiKey ? this.apiKey.length : 0 });
|
||||
logger.debug('Request payload size', { size: JSON.stringify(cleanData).length });
|
||||
logger.debug('Request headers', {
|
||||
contentType: 'application/json',
|
||||
hasApiKey: !!this.apiKey,
|
||||
apiKeyPrefix: this.apiKey ? this.apiKey.substring(0, 4) + '...' : 'none'
|
||||
});
|
||||
|
||||
logger.debug('Webhook response', { status: response.status });
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetchWithTimeout(this.webhookUrl, {
|
||||
method: 'POST',
|
||||
timeout: 30000, // 30 seconds
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': this.apiKey
|
||||
},
|
||||
body: JSON.stringify(cleanData),
|
||||
});
|
||||
|
||||
logger.debug('Webhook response received', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Object.fromEntries(response.headers.entries())
|
||||
});
|
||||
} catch (fetchError) {
|
||||
logger.error('Failed to send webhook request', {
|
||||
error: fetchError instanceof Error ? fetchError.message : String(fetchError),
|
||||
errorType: fetchError instanceof Error ? fetchError.name : typeof fetchError,
|
||||
url: this.webhookUrl,
|
||||
stack: fetchError instanceof Error ? fetchError.stack : undefined
|
||||
});
|
||||
throw fetchError;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user