diff --git a/app/api/groups/route.ts b/app/api/groups/route.ts index af307ad9..4f7e88d3 100644 --- a/app/api/groups/route.ts +++ b/app/api/groups/route.ts @@ -1,6 +1,7 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/options"; import { NextResponse } from "next/server"; +import { logger } from '@/lib/logger'; async function getAdminToken() { try { @@ -21,25 +22,28 @@ async function getAdminToken() { const data = await tokenResponse.json(); - // Log the response for debugging - console.log('Token Response:', { + // Log the response for debugging (without exposing the full token!) + logger.debug('Token Response', { status: tokenResponse.status, ok: tokenResponse.ok, - data: data + hasToken: !!data.access_token, + expiresIn: data.expires_in }); if (!tokenResponse.ok || !data.access_token) { - // Log the error details - console.error('Token Error Details:', { + // Log the error details (without sensitive data) + logger.error('Token Error Details', { status: tokenResponse.status, - data: data + error: data.error || 'Unknown error' }); return null; } return data.access_token; } catch (error) { - console.error('Token Error:', error); + logger.error('Token Error', { + error: error instanceof Error ? error.message : String(error) + }); return null; } } @@ -112,7 +116,9 @@ export async function GET() { return NextResponse.json(groupsWithCounts); } catch (error) { - console.error('Groups API Error:', error); + logger.error('Groups API Error', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ message: "Une erreur est survenue" }, { status: 500 }); } } @@ -157,7 +163,9 @@ export async function POST(req: Request) { membersCount: 0 }); } catch (error) { - console.error('Create Group Error:', error); + logger.error('Create Group Error', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json( { message: error instanceof Error ? error.message : "Une erreur est survenue" }, { status: 500 } diff --git a/app/api/missions/[missionId]/attachments/[attachmentId]/route.ts b/app/api/missions/[missionId]/attachments/[attachmentId]/route.ts index 45bb3750..5256d23a 100644 --- a/app/api/missions/[missionId]/attachments/[attachmentId]/route.ts +++ b/app/api/missions/[missionId]/attachments/[attachmentId]/route.ts @@ -3,15 +3,15 @@ import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { deleteMissionAttachment } from '@/lib/mission-uploads'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -72,7 +72,11 @@ export async function DELETE( return NextResponse.json({ success: true }); } catch (error) { - console.error('Error deleting attachment:', error); + logger.error('Error deleting attachment', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId, + attachmentId: params.attachmentId + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/app/api/missions/[missionId]/attachments/download/[attachmentId]/route.ts b/app/api/missions/[missionId]/attachments/download/[attachmentId]/route.ts index 55732a85..37d56fa1 100644 --- a/app/api/missions/[missionId]/attachments/download/[attachmentId]/route.ts +++ b/app/api/missions/[missionId]/attachments/download/[attachmentId]/route.ts @@ -5,15 +5,15 @@ import { prisma } from '@/lib/prisma'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { GetObjectCommand } from '@aws-sdk/client-s3'; import { s3Client, S3_CONFIG } from '@/lib/s3'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -76,7 +76,11 @@ export async function GET( // Redirect the user to the presigned URL for direct download return NextResponse.redirect(url); } catch (error) { - console.error('Error downloading attachment:', error); + logger.error('Error downloading attachment', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId, + attachmentId: params.attachmentId + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/app/api/missions/[missionId]/attachments/route.ts b/app/api/missions/[missionId]/attachments/route.ts index e2eaeff1..da8d6c11 100644 --- a/app/api/missions/[missionId]/attachments/route.ts +++ b/app/api/missions/[missionId]/attachments/route.ts @@ -3,15 +3,15 @@ import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { getPublicUrl, S3_CONFIG } from '@/lib/s3'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -69,7 +69,10 @@ export async function GET(request: Request, props: { params: Promise<{ missionId return NextResponse.json(attachmentsWithUrls); } catch (error) { - console.error('Error fetching mission attachments:', error); + logger.error('Error fetching mission attachments', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/app/api/missions/[missionId]/route.ts b/app/api/missions/[missionId]/route.ts index 184a0d1a..ae1caead 100644 --- a/app/api/missions/[missionId]/route.ts +++ b/app/api/missions/[missionId]/route.ts @@ -5,15 +5,15 @@ import { prisma } from '@/lib/prisma'; import { deleteMissionLogo, deleteMissionAttachment, getMissionFileUrl } from '@/lib/mission-uploads'; import { getPublicUrl, S3_CONFIG } from '@/lib/s3'; import { N8nService } from '@/lib/services/n8n-service'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -91,20 +91,17 @@ export async function GET(request: Request, props: { params: Promise<{ missionId })) }; - console.log('Mission data with URLs:', { + logger.debug('Mission data with URLs', { missionId: mission.id, - logoPath: mission.logo, - logoUrl: missionWithUrls.logoUrl, + hasLogo: !!mission.logo, attachmentCount: mission.attachments.length }); return NextResponse.json(missionWithUrls); } catch (error) { - console.error('Error retrieving mission:', { - error, - missionId: params.missionId, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + logger.error('Error retrieving mission', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId }); return NextResponse.json({ error: 'Internal server error', @@ -174,9 +171,8 @@ export async function PUT(request: Request, props: { params: Promise<{ missionId logoPath = `missions/${logo}`; } } catch (error) { - console.error('Error processing logo URL:', { - error, - logo, + logger.error('Error processing logo URL', { + error: error instanceof Error ? error.message : String(error), missionId }); } @@ -280,7 +276,10 @@ export async function PUT(request: Request, props: { params: Promise<{ missionId } }); } catch (error) { - console.error('Error updating mission:', error); + logger.error('Error updating mission', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) @@ -329,7 +328,7 @@ export async function DELETE( }); // Step 1: Trigger N8N workflow for deletion (rollback external integrations) - console.log('=== Starting N8N Deletion Workflow ==='); + logger.debug('Starting N8N deletion workflow'); const n8nService = new N8nService(); // Extract repo name from giteaRepositoryUrl if present @@ -341,9 +340,11 @@ export async function DELETE( // Extract repo name from path (last segment) const pathParts = url.pathname.split('/').filter(Boolean); repoName = pathParts[pathParts.length - 1] || ''; - console.log('Extracted repo name from URL:', { url: mission.giteaRepositoryUrl, repoName }); + logger.debug('Extracted repo name from URL', { repoName }); } catch (error) { - console.error('Error extracting repo name from URL:', error); + logger.error('Error extracting repo name from URL', { + error: error instanceof Error ? error.message : String(error) + }); // If URL parsing fails, try to extract from the string directly const match = mission.giteaRepositoryUrl.match(/\/([^\/]+)\/?$/); repoName = match ? match[1] : ''; @@ -370,13 +371,22 @@ export async function DELETE( } }; - console.log('Sending deletion data to N8N:', JSON.stringify(n8nDeletionData, null, 2)); + logger.debug('Sending deletion data to N8N', { + missionId: n8nDeletionData.missionId, + name: n8nDeletionData.name, + hasRepoName: !!n8nDeletionData.repoName + }); const n8nResult = await n8nService.triggerMissionDeletion(n8nDeletionData); - console.log('N8N Deletion Workflow Result:', JSON.stringify(n8nResult, null, 2)); + logger.debug('N8N deletion workflow result', { + success: n8nResult.success, + hasError: !!n8nResult.error + }); if (!n8nResult.success) { - console.error('N8N deletion workflow failed, but continuing with mission deletion:', n8nResult.error); + logger.error('N8N deletion workflow failed, but continuing with mission deletion', { + error: n8nResult.error + }); // Continue with deletion even if N8N fails (non-blocking) } @@ -385,22 +395,28 @@ export async function DELETE( if (mission.logo) { try { await deleteMissionLogo(params.missionId, mission.logo); - console.log('Logo deleted successfully from Minio'); + logger.debug('Logo deleted successfully from Minio'); } catch (error) { - console.error('Error deleting mission logo from Minio:', error); + logger.error('Error deleting mission logo from Minio', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId + }); // Continue deletion even if logo deletion fails } } // Delete attachments from Minio if (attachments.length > 0) { - console.log(`Deleting ${attachments.length} attachment(s) from Minio...`); + logger.debug(`Deleting ${attachments.length} attachment(s) from Minio`); for (const attachment of attachments) { try { await deleteMissionAttachment(attachment.filePath); - console.log(`Attachment deleted successfully: ${attachment.filename}`); + logger.debug('Attachment deleted successfully', { filename: attachment.filename }); } catch (error) { - console.error(`Error deleting attachment ${attachment.filename} from Minio:`, error); + logger.error('Error deleting attachment from Minio', { + error: error instanceof Error ? error.message : String(error), + filename: attachment.filename + }); // Continue deletion even if one attachment fails } } @@ -411,11 +427,14 @@ export async function DELETE( where: { id: params.missionId } }); - console.log('Mission deleted successfully from database'); + logger.debug('Mission deleted successfully from database', { missionId: params.missionId }); return NextResponse.json({ success: true }); } catch (error) { - console.error('Error deleting mission:', error); + logger.error('Error deleting mission', { + error: error instanceof Error ? error.message : String(error), + missionId: params.missionId + }); return NextResponse.json( { error: 'Failed to delete mission' }, { status: 500 } diff --git a/app/api/missions/all/route.ts b/app/api/missions/all/route.ts index adde3e86..b7ea4402 100644 --- a/app/api/missions/all/route.ts +++ b/app/api/missions/all/route.ts @@ -4,15 +4,15 @@ import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { getPublicUrl } from '@/lib/s3'; import { S3_CONFIG } from '@/lib/s3'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -87,10 +87,9 @@ export async function GET(request: Request) { // Transform missions to include public URLs (same format as /api/missions) const missionsWithPublicUrls = missions.map(mission => { - console.log('Processing mission logo:', { + logger.debug('Processing mission logo', { missionId: mission.id, - logo: mission.logo, - constructedUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null + hasLogo: !!mission.logo }); return { @@ -109,7 +108,9 @@ export async function GET(request: Request) { } }); } catch (error) { - console.error('Error listing all missions:', error); + logger.error('Error listing all missions', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/app/api/missions/image/[...path]/route.ts b/app/api/missions/image/[...path]/route.ts index 6ade181d..69f35a19 100644 --- a/app/api/missions/image/[...path]/route.ts +++ b/app/api/missions/image/[...path]/route.ts @@ -3,6 +3,7 @@ import { getServerSession } from 'next-auth'; import { authOptions } from '@/app/api/auth/options'; import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; import { NoSuchKey } from '@aws-sdk/client-s3'; +import { logger } from '@/lib/logger'; // Initialize S3 client const s3Client = new S3Client({ @@ -23,23 +24,22 @@ export async function GET( try { const { path: pathSegments } = await params; if (!pathSegments || pathSegments.length === 0) { - console.error('No path segments provided'); + logger.error('No path segments provided'); return new NextResponse('Path is required', { status: 400 }); } // Reconstruct the full path from path segments const filePath = pathSegments.join('/'); - console.log('Fetching mission image:', { + logger.debug('Fetching mission image', { originalPath: filePath, segments: pathSegments }); // Remove the missions/ prefix from the URL path since the file is already in the missions bucket const minioPath = filePath.replace(/^missions\//, ''); - console.log('Full Minio path:', { + logger.debug('Full Minio path', { minioPath, - bucket: 'missions', - endpoint: 'https://dome-api.slm-lab.net' + bucket: 'missions' }); const command = new GetObjectCommand({ @@ -50,11 +50,10 @@ export async function GET( try { const response = await s3Client.send(command); if (!response.Body) { - console.error('File not found in Minio:', { + logger.error('File not found in Minio', { path: filePath, minioPath, - bucket: 'missions', - contentType: response.ContentType + bucket: 'missions' }); return new NextResponse('File not found', { status: 404 }); } @@ -65,24 +64,20 @@ export async function GET( headers.set('Content-Type', contentType); headers.set('Cache-Control', 'public, max-age=31536000'); - // Log the response details - console.log('Serving image:', { + logger.debug('Serving image', { path: filePath, minioPath, contentType, - contentLength: response.ContentLength, - lastModified: response.LastModified, - etag: response.ETag + contentLength: response.ContentLength }); return new NextResponse(response.Body as any, { headers }); } catch (error) { - console.error('Error fetching file from Minio:', { - error, + logger.error('Error fetching file from Minio', { + error: error instanceof Error ? error.message : String(error), path: filePath, minioPath, bucket: 'missions', - endpoint: 'https://dome-api.slm-lab.net', errorType: error instanceof NoSuchKey ? 'NoSuchKey' : 'Unknown' }); if (error instanceof NoSuchKey) { @@ -91,10 +86,8 @@ export async function GET( return new NextResponse('Internal Server Error', { status: 500 }); } } catch (error) { - console.error('Error in image serving:', { - error, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + logger.error('Error in image serving', { + error: error instanceof Error ? error.message : String(error) }); return new NextResponse('Internal Server Error', { status: 500 }); } diff --git a/app/api/missions/mission-created/route.ts b/app/api/missions/mission-created/route.ts index b922e757..d61f49f8 100644 --- a/app/api/missions/mission-created/route.ts +++ b/app/api/missions/mission-created/route.ts @@ -1,5 +1,6 @@ import { NextResponse } from 'next/server'; import { prisma } from '@/lib/prisma'; +import { logger } from '@/lib/logger'; /** * POST /api/missions/mission-created @@ -24,14 +25,14 @@ import { prisma } from '@/lib/prisma'; */ export async function POST(request: Request) { try { - console.log('=== Mission Created Webhook Received ==='); + logger.debug('Mission Created Webhook Received'); // Vérifier l'API key const apiKey = request.headers.get('x-api-key'); const expectedApiKey = process.env.N8N_API_KEY; if (!expectedApiKey) { - console.error('N8N_API_KEY not configured in environment'); + logger.error('N8N_API_KEY not configured in environment'); return NextResponse.json( { error: 'Server configuration error' }, { status: 500 } @@ -39,7 +40,7 @@ export async function POST(request: Request) { } if (apiKey !== expectedApiKey) { - console.error('Invalid API key:', { + logger.error('Invalid API key', { received: apiKey ? 'present' : 'missing', expected: expectedApiKey ? 'configured' : 'missing' }); @@ -50,7 +51,11 @@ export async function POST(request: Request) { } const body = await request.json(); - console.log('Received mission-created data:', JSON.stringify(body, null, 2)); + logger.debug('Received mission-created data', { + hasMissionId: !!body.missionId, + hasName: !!body.name, + hasCreatorId: !!body.creatorId + }); // Validation des champs requis // Prefer missionId if provided, otherwise use name + creatorId @@ -58,13 +63,13 @@ export async function POST(request: Request) { if (body.missionId) { // ✅ Use missionId if provided (more reliable) - console.log('Looking up mission by ID:', body.missionId); + logger.debug('Looking up mission by ID', { missionId: body.missionId }); mission = await prisma.mission.findUnique({ where: { id: body.missionId } }); } else if (body.name && body.creatorId) { // Fallback to name + creatorId (for backward compatibility) - console.log('Looking up mission by name + creatorId:', { + logger.debug('Looking up mission by name + creatorId', { name: body.name, creatorId: body.creatorId }); @@ -78,7 +83,7 @@ export async function POST(request: Request) { } }); } else { - console.error('Missing required fields:', { + logger.error('Missing required fields', { hasMissionId: !!body.missionId, hasName: !!body.name, hasCreatorId: !!body.creatorId @@ -90,7 +95,7 @@ export async function POST(request: Request) { } if (!mission) { - console.error('Mission not found:', { + logger.error('Mission not found', { missionId: body.missionId, name: body.name, creatorId: body.creatorId @@ -101,14 +106,14 @@ export async function POST(request: Request) { ); } - console.log('Found mission:', { + logger.debug('Found mission', { id: mission.id, name: mission.name, - currentIntegrationIds: { - gitea: mission.giteaRepositoryUrl, - leantime: mission.leantimeProjectId, - outline: mission.outlineCollectionId, - rocketChat: mission.rocketChatChannelId + hasIntegrations: { + gitea: !!mission.giteaRepositoryUrl, + leantime: !!mission.leantimeProjectId, + outline: !!mission.outlineCollectionId, + rocketChat: !!mission.rocketChatChannelId } }); @@ -123,7 +128,7 @@ export async function POST(request: Request) { // Mapper les champs N8N vers notre schéma Prisma if (body.gitRepoUrl !== undefined) { updateData.giteaRepositoryUrl = body.gitRepoUrl || null; - console.log('Updating giteaRepositoryUrl:', body.gitRepoUrl); + logger.debug('Updating giteaRepositoryUrl', { hasUrl: !!body.gitRepoUrl }); } if (body.leantimeProjectId !== undefined) { @@ -131,22 +136,22 @@ export async function POST(request: Request) { updateData.leantimeProjectId = body.leantimeProjectId ? String(body.leantimeProjectId) : null; - console.log('Updating leantimeProjectId:', updateData.leantimeProjectId); + logger.debug('Updating leantimeProjectId', { hasId: !!updateData.leantimeProjectId }); } if (body.documentationCollectionId !== undefined) { updateData.outlineCollectionId = body.documentationCollectionId || null; - console.log('Updating outlineCollectionId:', updateData.outlineCollectionId); + logger.debug('Updating outlineCollectionId', { hasId: !!updateData.outlineCollectionId }); } if (body.rocketchatChannelId !== undefined) { updateData.rocketChatChannelId = body.rocketchatChannelId || null; - console.log('Updating rocketChatChannelId:', updateData.rocketChatChannelId); + logger.debug('Updating rocketChatChannelId', { hasId: !!updateData.rocketChatChannelId }); } // Vérifier qu'il y a au moins un champ à mettre à jour if (Object.keys(updateData).length === 0) { - console.warn('No integration IDs to update'); + logger.warn('No integration IDs to update'); return NextResponse.json({ message: 'Mission found but no integration IDs provided', mission: { @@ -162,16 +167,10 @@ export async function POST(request: Request) { data: updateData }); - console.log('Mission updated successfully:', { + logger.debug('Mission updated successfully', { id: updatedMission.id, name: updatedMission.name, - updatedFields: Object.keys(updateData), - newIntegrationIds: { - gitea: updatedMission.giteaRepositoryUrl, - leantime: updatedMission.leantimeProjectId, - outline: updatedMission.outlineCollectionId, - rocketChat: updatedMission.rocketChatChannelId - } + updatedFields: Object.keys(updateData) }); return NextResponse.json({ @@ -188,7 +187,9 @@ export async function POST(request: Request) { }); } catch (error) { - console.error('Error in mission-created webhook:', error); + logger.error('Error in mission-created webhook', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json( { error: 'Failed to update mission', diff --git a/app/api/missions/route.ts b/app/api/missions/route.ts index ed796b9b..a364a509 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -7,6 +7,7 @@ import { Prisma } from '@prisma/client'; import { s3Client } from '@/lib/s3'; import { CopyObjectCommand, DeleteObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3'; import { uploadMissionLogo, uploadMissionAttachment, getMissionFileUrl } from '@/lib/mission-uploads'; +import { logger } from '@/lib/logger'; // Types interface MissionCreateInput { @@ -153,9 +154,9 @@ export async function GET(request: Request) { // Transform missions to include public URLs const missionsWithUrls = missions.map(mission => { - console.log('Processing mission logo:', { + logger.debug('Processing mission logo:', { missionId: mission.id, - logo: mission.logo, + hasLogo: !!mission.logo, constructedUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null }); @@ -179,7 +180,9 @@ export async function GET(request: Request) { } }); } catch (error) { - console.error('Error listing missions:', error); + logger.error('Error listing missions', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) @@ -196,7 +199,10 @@ async function verifyFileExists(filePath: string): Promise { })); return true; } catch (error) { - console.error('Error verifying file:', filePath, error); + logger.error('Error verifying file:', { + filePath, + error: error instanceof Error ? error.message : String(error) + }); return false; } } @@ -206,14 +212,19 @@ export async function POST(request: Request) { let uploadedFiles: { type: 'logo' | 'attachment', path: string }[] = []; try { - console.log('=== Mission Creation Started ==='); + logger.debug('Mission creation started'); const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const body = await request.json(); - console.log('Received request body:', JSON.stringify(body, null, 2)); + logger.debug('Received mission creation request', { + hasName: !!body.name, + hasOddScope: !!body.oddScope, + hasLogo: !!body.logo?.data, + attachmentsCount: body.attachments?.length || 0 + }); // Simple validation if (!body.name || !body.oddScope) { @@ -239,13 +250,21 @@ export async function POST(request: Request) { logo: null, // Will update after upload }; - console.log('Creating mission with data:', JSON.stringify(missionData, null, 2)); + logger.debug('Creating mission', { + name: missionData.name, + oddScope: missionData.oddScope, + niveau: missionData.niveau, + missionType: missionData.missionType + }); const mission = await prisma.mission.create({ data: missionData }); - console.log('Mission created successfully:', JSON.stringify(mission, null, 2)); + logger.debug('Mission created successfully', { + missionId: mission.id, + name: mission.name + }); // Step 2: Create mission users (guardians and volunteers) const missionUsers = []; @@ -279,7 +298,10 @@ export async function POST(request: Request) { await prisma.missionUser.createMany({ data: missionUsers }); - console.log('Mission users created:', missionUsers); + logger.debug('Mission users created', { + count: missionUsers.length, + roles: missionUsers.map(u => u.role) + }); } // Step 3: Upload logo to Minio if present @@ -311,13 +333,15 @@ export async function POST(request: Request) { data: { logo: filePath } }); - console.log('Logo uploaded successfully:', { + logger.debug('Logo uploaded successfully', { logoPath, - logoUrl, - baseUrl + hasLogoUrl: !!logoUrl }); } catch (uploadError) { - console.error('Error uploading logo:', uploadError); + logger.error('Error uploading logo', { + error: uploadError instanceof Error ? uploadError.message : String(uploadError), + missionId: mission.id + }); throw new Error('Failed to upload logo'); } } @@ -348,9 +372,14 @@ export async function POST(request: Request) { }); await Promise.all(attachmentPromises); - console.log('Attachments uploaded successfully'); + logger.debug('Attachments uploaded successfully', { + count: body.attachments.length + }); } catch (attachmentError) { - console.error('Error uploading attachments:', attachmentError); + logger.error('Error uploading attachments', { + error: attachmentError instanceof Error ? attachmentError.message : String(attachmentError), + missionId: mission.id + }); throw new Error('Failed to upload attachments'); } } @@ -378,7 +407,7 @@ export async function POST(request: Request) { } // Only trigger n8n after verifying all files - console.log('=== Starting N8N Workflow ==='); + logger.debug('Starting N8N workflow'); const n8nService = new N8nService(); const n8nData = { @@ -392,10 +421,17 @@ export async function POST(request: Request) { MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL } }; - console.log('Sending to N8N:', JSON.stringify(n8nData, null, 2)); + logger.debug('Sending to N8N', { + missionId: n8nData.missionId, + name: n8nData.name, + hasLogo: !!n8nData.logoPath + }); const workflowResult = await n8nService.triggerMissionCreation(n8nData); - console.log('N8N Workflow Result:', JSON.stringify(workflowResult, null, 2)); + logger.debug('N8N workflow result', { + success: workflowResult.success, + hasError: !!workflowResult.error + }); if (!workflowResult.success) { throw new Error(workflowResult.error || 'N8N workflow failed'); @@ -407,11 +443,16 @@ export async function POST(request: Request) { message: 'Mission created successfully with all integrations' }); } catch (error) { - console.error('Error in final verification or n8n:', error); + logger.error('Error in final verification or n8n', { + error: error instanceof Error ? error.message : String(error) + }); throw error; } } catch (error) { - console.error('Error in mission creation:', error); + logger.error('Error in mission creation', { + error: error instanceof Error ? error.message : String(error), + uploadedFilesCount: uploadedFiles.length + }); // Cleanup: Delete any uploaded files for (const file of uploadedFiles) { @@ -420,9 +461,12 @@ export async function POST(request: Request) { Bucket: 'missions', Key: file.path.replace('missions/', '') })); - console.log('Cleaned up file:', file.path); + logger.debug('Cleaned up file', { path: file.path }); } catch (cleanupError) { - console.error('Error cleaning up file:', file.path, cleanupError); + logger.error('Error cleaning up file', { + path: file.path, + error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError) + }); } } diff --git a/app/api/missions/upload/route.ts b/app/api/missions/upload/route.ts index 03ace7a8..f05fd603 100644 --- a/app/api/missions/upload/route.ts +++ b/app/api/missions/upload/route.ts @@ -10,15 +10,15 @@ import { generateMissionAttachmentUploadUrl } from '@/lib/mission-uploads'; import { getPublicUrl, S3_CONFIG } from '@/lib/s3'; +import { logger } from '@/lib/logger'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { - console.error('Unauthorized access attempt:', { + logger.error('Unauthorized access attempt', { url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers) + method: request.method }); return { authorized: false, userId: null }; } @@ -75,7 +75,9 @@ export async function GET(request: Request) { return NextResponse.json(result); } catch (error) { - console.error('Error generating upload URL:', error); + logger.error('Error generating upload URL', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) @@ -85,25 +87,21 @@ export async function GET(request: Request) { // Handle file upload (server-side) export async function POST(request: Request) { - console.log('=== File upload request received ==='); + logger.debug('File upload request received'); try { - console.log('Checking authentication...'); const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { - console.log('Authentication failed'); return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } - console.log('User authenticated:', userId); // Parse the form data - console.log('Parsing form data...'); const formData = await request.formData(); const missionId = formData.get('missionId') as string; const type = formData.get('type') as string; // 'logo' or 'attachment' const file = formData.get('file') as File; - console.log('Form data received:', { + logger.debug('Form data received', { missionId, type, fileExists: !!file, @@ -113,7 +111,7 @@ export async function POST(request: Request) { }); if (!missionId || !type || !file) { - console.log('Missing required fields:', { missionId: !!missionId, type: !!type, file: !!file }); + logger.error('Missing required fields', { missionId: !!missionId, type: !!type, file: !!file }); return NextResponse.json({ error: 'Missing required fields', required: { missionId: true, type: true, file: true }, @@ -122,42 +120,38 @@ export async function POST(request: Request) { } // Verify that the mission exists and user has access to it - console.log('Verifying mission access...'); const mission = await prisma.mission.findUnique({ where: { id: missionId }, select: { id: true, creatorId: true } }); if (!mission) { - console.log('Mission not found:', missionId); + logger.error('Mission not found', { missionId }); return NextResponse.json({ error: 'Mission not found' }, { status: 404 }); } // Currently only allow creator to upload files if (mission.creatorId !== userId) { - console.log('User not authorized to upload to this mission', { userId, creatorId: mission.creatorId }); + logger.error('User not authorized to upload to this mission', { userId, creatorId: mission.creatorId }); return NextResponse.json({ error: 'Not authorized to upload to this mission' }, { status: 403 }); } - console.log('Mission access verified'); if (type === 'logo') { - console.log('Processing logo upload...'); + logger.debug('Processing logo upload'); try { // Upload logo file to Minio const { filePath } = await uploadMissionLogo(userId, missionId, file); - console.log('Logo uploaded successfully to path:', filePath); + logger.debug('Logo uploaded successfully', { filePath }); // Generate public URL - remove missions/ prefix since it's added by the API const publicUrl = `/api/missions/image/${filePath.replace('missions/', '')}`; - console.log('Public URL for logo:', publicUrl); // Update mission record with logo path - console.log('Updating mission record with logo path...'); await prisma.mission.update({ where: { id: missionId }, data: { logo: filePath } }); - console.log('Mission record updated'); + logger.debug('Mission record updated with logo'); return NextResponse.json({ success: true, @@ -165,7 +159,10 @@ export async function POST(request: Request) { publicUrl }); } catch (logoError) { - console.error('Error in logo upload process:', logoError); + logger.error('Error in logo upload process', { + error: logoError instanceof Error ? logoError.message : String(logoError), + missionId + }); return NextResponse.json({ error: 'Logo upload failed', details: logoError instanceof Error ? logoError.message : String(logoError) @@ -174,21 +171,19 @@ export async function POST(request: Request) { } else if (type === 'attachment') { // Upload attachment file to Minio - console.log('Processing attachment upload...'); + logger.debug('Processing attachment upload'); try { const { filename, filePath, fileType, fileSize } = await uploadMissionAttachment( userId, missionId, file ); - console.log('Attachment uploaded successfully to path:', filePath); + logger.debug('Attachment uploaded successfully', { filePath }); // Generate public URL const publicUrl = getPublicUrl(filePath, S3_CONFIG.bucket); - console.log('Public URL for attachment:', publicUrl); // Create attachment record in database - console.log('Creating attachment record in database...'); const attachment = await prisma.attachment.create({ data: { filename, @@ -199,7 +194,7 @@ export async function POST(request: Request) { uploaderId: userId } }); - console.log('Attachment record created:', attachment.id); + logger.debug('Attachment record created', { attachmentId: attachment.id }); return NextResponse.json({ success: true, @@ -214,7 +209,10 @@ export async function POST(request: Request) { } }); } catch (attachmentError) { - console.error('Error in attachment upload process:', attachmentError); + logger.error('Error in attachment upload process', { + error: attachmentError instanceof Error ? attachmentError.message : String(attachmentError), + missionId + }); return NextResponse.json({ error: 'Attachment upload failed', details: attachmentError instanceof Error ? attachmentError.message : String(attachmentError) @@ -222,11 +220,13 @@ export async function POST(request: Request) { } } else { - console.log('Invalid upload type:', type); + logger.error('Invalid upload type', { type }); return NextResponse.json({ error: 'Invalid upload type' }, { status: 400 }); } } catch (error) { - console.error('Unhandled error in upload process:', error); + logger.error('Unhandled error in upload process', { + error: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/components/missions/missions-admin-panel.tsx b/components/missions/missions-admin-panel.tsx index a1c820d7..4dbd8208 100644 --- a/components/missions/missions-admin-panel.tsx +++ b/components/missions/missions-admin-panel.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState, useEffect } from "react"; +import { logger } from '@/lib/logger'; import { Tabs, TabsContent, @@ -115,7 +116,7 @@ export function MissionsAdminPanel() { try { await Promise.all([fetchUsers(), fetchGroups()]); } catch (error) { - console.error("Error fetching data:", error); + logger.error("Error fetching data", { error: error instanceof Error ? error.message : String(error) }); } finally { setLoading(false); } @@ -134,7 +135,7 @@ export function MissionsAdminPanel() { const data = await response.json(); setUsers(data); } catch (error) { - console.error("Error fetching users:", error); + logger.error("Error fetching users", { error: error instanceof Error ? error.message : String(error) }); toast({ title: "Erreur", description: "Erreur lors de la récupération des utilisateurs", @@ -166,7 +167,7 @@ export function MissionsAdminPanel() { } return {...group, membersCount: 0}; } catch (error) { - console.error(`Error fetching members for group ${group.id}:`, error); + logger.error(`Error fetching members for group ${group.id}`, { error: error instanceof Error ? error.message : String(error) }); return {...group, membersCount: 0}; } }) @@ -174,7 +175,7 @@ export function MissionsAdminPanel() { setGroups(groupsWithCounts); } catch (error) { - console.error("Error fetching groups:", error); + logger.error("Error fetching groups", { error: error instanceof Error ? error.message : String(error) }); toast({ title: "Erreur", description: "Erreur lors de la récupération des groupes", @@ -292,7 +293,7 @@ export function MissionsAdminPanel() { const data = await response.json(); return data; } catch (error) { - console.error(`Error fetching members for group ${groupId}:`, error); + logger.error(`Error fetching members for group ${groupId}`, { error: error instanceof Error ? error.message : String(error) }); toast({ title: "Erreur", description: "Erreur lors de la récupération des membres du groupe", @@ -325,7 +326,7 @@ export function MissionsAdminPanel() { }); } } catch (error) { - console.error("Error handling group members:", error); + logger.error("Error handling group members", { error: error instanceof Error ? error.message : String(error) }); toast({ title: "Erreur", description: "Erreur lors de l'affichage des membres du groupe", @@ -421,8 +422,6 @@ export function MissionsAdminPanel() { logo: missionData.logo // Ensure logo data is included }; - console.log('Submitting mission data:', JSON.stringify(missionSubmitData, null, 2)); - // Send to API const response = await fetch('/api/missions', { method: 'POST', @@ -448,7 +447,6 @@ export function MissionsAdminPanel() { router.push('/missions'); } catch (error) { - console.error('Error creating mission:', error); toast({ title: "Erreur", description: error instanceof Error ? error.message : "Une erreur est survenue lors de la création de la mission", @@ -498,7 +496,6 @@ export function MissionsAdminPanel() { type="logo" isNewMission={true} onFileSelect={(fileData) => { - console.log('Logo file selected:', fileData); setMissionData(prev => ({ ...prev, logo: fileData diff --git a/lib/mission-uploads.ts b/lib/mission-uploads.ts index 882d18ec..28794171 100644 --- a/lib/mission-uploads.ts +++ b/lib/mission-uploads.ts @@ -3,6 +3,7 @@ */ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { logger } from '@/lib/logger'; // Initialize S3 client for Minio const s3Client = new S3Client({ @@ -45,10 +46,8 @@ export async function deleteMissionLogo(missionId: string, logoPath: string): Pr const normalizedPath = ensureMissionsPrefix(logoPath); const minioPath = normalizedPath.replace(/^missions\//, ''); // Remove prefix for Minio - console.log('Deleting mission logo:', { + logger.debug('Deleting mission logo', { missionId, - originalPath: logoPath, - normalizedPath, minioPath }); @@ -59,14 +58,12 @@ export async function deleteMissionLogo(missionId: string, logoPath: string): Pr await s3Client.send(command); - console.log('Mission logo deleted successfully:', minioPath); + logger.debug('Mission logo deleted successfully', { minioPath }); } catch (error) { - console.error('Error deleting mission logo:', { - error, + logger.error('Error deleting mission logo', { + error: error instanceof Error ? error.message : String(error), missionId, - logoPath, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + minioPath }); throw error; } @@ -79,9 +76,7 @@ export async function deleteMissionAttachment(filePath: string): Promise { const normalizedPath = ensureMissionsPrefix(filePath); const minioPath = normalizedPath.replace(/^missions\//, ''); // Remove prefix for Minio - console.log('Deleting mission attachment:', { - originalPath: filePath, - normalizedPath, + logger.debug('Deleting mission attachment', { minioPath }); @@ -92,13 +87,11 @@ export async function deleteMissionAttachment(filePath: string): Promise { await s3Client.send(command); - console.log('Mission attachment deleted successfully:', minioPath); + logger.debug('Mission attachment deleted successfully', { minioPath }); } catch (error) { - console.error('Error deleting mission attachment:', { - error, - filePath, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + logger.error('Error deleting mission attachment', { + error: error instanceof Error ? error.message : String(error), + minioPath }); throw error; } @@ -107,8 +100,7 @@ export async function deleteMissionAttachment(filePath: string): Promise { // Upload a mission logo to Minio export async function uploadMissionLogo(userId: string, missionId: string, file: File): Promise<{ filePath: string }> { try { - console.log('Starting logo upload:', { - userId, + logger.debug('Starting logo upload', { missionId, fileName: file.name, fileSize: file.size, @@ -123,7 +115,7 @@ export async function uploadMissionLogo(userId: string, missionId: string, file: const arrayBuffer = await file.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); - console.log('Uploading to Minio:', { + logger.debug('Uploading to Minio', { bucket: 'missions', key: minioPath, contentType: file.type @@ -137,20 +129,17 @@ export async function uploadMissionLogo(userId: string, missionId: string, file: ACL: 'public-read' })); - console.log('Logo upload successful:', { + logger.debug('Logo upload successful', { filePath, minioPath }); return { filePath }; } catch (error) { - console.error('Error uploading mission logo:', { - error, - userId, + logger.error('Error uploading mission logo', { + error: error instanceof Error ? error.message : String(error), missionId, - fileName: file.name, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + fileName: file.name }); throw error; } @@ -168,8 +157,7 @@ export async function uploadMissionAttachment( fileSize: number; }> { try { - console.log('Starting attachment upload:', { - userId, + logger.debug('Starting attachment upload', { missionId, fileName: file.name, fileSize: file.size, @@ -183,7 +171,7 @@ export async function uploadMissionAttachment( const arrayBuffer = await file.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); - console.log('Uploading to Minio:', { + logger.debug('Uploading to Minio', { bucket: 'missions', key: minioPath, contentType: file.type @@ -197,7 +185,7 @@ export async function uploadMissionAttachment( ACL: 'public-read' })); - console.log('Attachment upload successful:', { + logger.debug('Attachment upload successful', { filePath, minioPath }); @@ -209,13 +197,10 @@ export async function uploadMissionAttachment( fileSize: file.size }; } catch (error) { - console.error('Error uploading mission attachment:', { - error, - userId, + logger.error('Error uploading mission attachment', { + error: error instanceof Error ? error.message : String(error), missionId, - fileName: file.name, - errorType: error instanceof Error ? error.constructor.name : typeof error, - message: error instanceof Error ? error.message : String(error) + fileName: file.name }); throw error; } diff --git a/lib/services/n8n-service.ts b/lib/services/n8n-service.ts index 66508fc7..3e8340c2 100644 --- a/lib/services/n8n-service.ts +++ b/lib/services/n8n-service.ts @@ -1,4 +1,5 @@ import { env } from '@/lib/env'; +import { logger } from '@/lib/logger'; export class N8nService { private webhookUrl: string; @@ -12,7 +13,7 @@ export class N8nService { this.apiKey = process.env.N8N_API_KEY || ''; if (!this.apiKey) { - console.error('N8N_API_KEY is not set in environment variables'); + logger.error('N8N_API_KEY is not set in environment variables'); } } @@ -20,9 +21,13 @@ export class N8nService { try { const deleteWebhookUrl = process.env.N8N_DELETE_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook-test/mission-delete'; - console.log('Triggering n8n mission deletion workflow with data:', JSON.stringify(data, null, 2)); - console.log('Using deletion webhook URL:', deleteWebhookUrl); - console.log('API key present:', !!this.apiKey); + logger.debug('Triggering n8n mission deletion workflow', { + missionId: data.missionId, + name: data.name, + hasRepoName: !!data.repoName + }); + logger.debug('Using deletion webhook URL', { url: deleteWebhookUrl }); + logger.debug('API key present', { present: !!this.apiKey }); const response = await fetch(deleteWebhookUrl, { method: 'POST', @@ -33,29 +38,39 @@ export class N8nService { body: JSON.stringify(data), }); - console.log('Deletion webhook response status:', response.status); - console.log('Deletion webhook response headers:', Object.fromEntries(response.headers.entries())); + logger.debug('Deletion webhook response', { status: response.status }); if (!response.ok) { const errorText = await response.text(); - console.error('Deletion webhook error response:', errorText); + logger.error('Deletion webhook error response', { + status: response.status, + error: errorText.substring(0, 200) // Truncate to avoid logging huge responses + }); // Try to parse the error response as JSON for more details try { const errorJson = JSON.parse(errorText); - console.error('Parsed error response:', errorJson); + logger.error('Parsed error response', { + code: errorJson.code, + message: errorJson.message + }); } catch (e) { - console.error('Error response is not JSON'); + logger.error('Error response is not JSON'); } throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); } const responseText = await response.text(); - console.log('N8nService - Deletion raw response:', responseText); + logger.debug('N8nService - Deletion raw response received', { + length: responseText.length + }); // Try to parse the response as JSON try { const result = JSON.parse(responseText); - console.log('Parsed deletion workflow result:', JSON.stringify(result, null, 2)); + logger.debug('Parsed deletion workflow result', { + success: result.success || !result.error, + hasError: !!result.error + }); // Check if the response contains error information if (result.error || result.message?.includes('failed')) { @@ -70,14 +85,16 @@ export class N8nService { results: result }; } catch (parseError) { - console.log('Response is not JSON, treating as workflow trigger confirmation'); + logger.debug('Response is not JSON, treating as workflow trigger confirmation'); return { success: true, results: { confirmed: true } }; } } catch (error) { - console.error('Error triggering n8n deletion workflow:', error); + logger.error('Error triggering n8n deletion workflow', { + error: error instanceof Error ? error.message : String(error) + }); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' @@ -87,12 +104,11 @@ export class N8nService { async triggerMissionCreation(data: any): Promise { try { - console.log('N8nService - Input data:', { + logger.debug('N8nService - Input data', { hasServices: Array.isArray(data.services), services: data.services, hasGite: data.services?.includes('Gite'), - missionProcessed: data.missionProcessed, - config: data.config + missionId: data.missionId }); // Clean and validate the data @@ -122,12 +138,11 @@ export class N8nService { }; // Log the cleaned data - console.log('Sending cleaned data to n8n:', { + logger.debug('Sending cleaned data to n8n', { name: cleanData.name, - creatorId: cleanData.creatorId, + missionId: cleanData.missionId, oddScope: cleanData.oddScope, niveau: cleanData.niveau, - intention: cleanData.intention?.substring(0, 100) + '...', // Log first 100 chars missionType: cleanData.missionType, donneurDOrdre: cleanData.donneurDOrdre, projection: cleanData.projection, @@ -136,12 +151,11 @@ export class N8nService { profils: cleanData.profils, hasGuardians: !!cleanData.guardians, volunteersCount: cleanData.volunteers.length, - hasConfig: !!cleanData.config, - configKeys: cleanData.config ? Object.keys(cleanData.config) : [] + hasLogo: !!cleanData.logoPath }); - console.log('Using webhook URL:', this.webhookUrl); - console.log('API key present:', !!this.apiKey); + logger.debug('Using webhook URL', { url: this.webhookUrl }); + logger.debug('API key present', { present: !!this.apiKey }); const response = await fetch(this.webhookUrl, { method: 'POST', @@ -152,29 +166,39 @@ export class N8nService { body: JSON.stringify(cleanData), }); - console.log('Webhook response status:', response.status); - console.log('Webhook response headers:', Object.fromEntries(response.headers.entries())); + logger.debug('Webhook response', { status: response.status }); if (!response.ok) { const errorText = await response.text(); - console.error('Webhook error response:', errorText); + logger.error('Webhook error response', { + status: response.status, + error: errorText.substring(0, 200) // Truncate to avoid logging huge responses + }); // Try to parse the error response as JSON for more details try { const errorJson = JSON.parse(errorText); - console.error('Parsed error response:', errorJson); + logger.error('Parsed error response', { + code: errorJson.code, + message: errorJson.message + }); } catch (e) { - console.error('Error response is not JSON'); + logger.error('Error response is not JSON'); } throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); } const responseText = await response.text(); - console.log('N8nService - Raw response:', responseText); + logger.debug('N8nService - Raw response received', { + length: responseText.length + }); // Try to parse the response as JSON try { const result = JSON.parse(responseText); - console.log('Parsed workflow result:', JSON.stringify(result, null, 2)); + logger.debug('Parsed workflow result', { + success: !result.error && !result.message?.includes('failed'), + hasError: !!result.error + }); // Check if the response contains error information if (result.error || result.message?.includes('failed')) { @@ -202,7 +226,7 @@ export class N8nService { results: result }; } catch (parseError) { - console.log('Response is not JSON, treating as workflow trigger confirmation'); + logger.debug('Response is not JSON, treating as workflow trigger confirmation'); return { success: true, results: { @@ -215,7 +239,9 @@ export class N8nService { }; } } catch (error) { - console.error('Error triggering n8n workflow:', error); + logger.error('Error triggering n8n workflow', { + error: error instanceof Error ? error.message : String(error) + }); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' @@ -225,9 +251,12 @@ export class N8nService { async triggerMissionRollback(data: any): Promise { try { - console.log('Triggering n8n rollback workflow with data:', JSON.stringify(data, null, 2)); - console.log('Using rollback webhook URL:', this.rollbackWebhookUrl); - console.log('API key present:', !!this.apiKey); + logger.debug('Triggering n8n rollback workflow', { + missionId: data.missionId, + name: data.name + }); + logger.debug('Using rollback webhook URL', { url: this.rollbackWebhookUrl }); + logger.debug('API key present', { present: !!this.apiKey }); const response = await fetch(this.rollbackWebhookUrl, { method: 'POST', @@ -240,19 +269,27 @@ export class N8nService { if (!response.ok) { const errorText = await response.text(); - console.error('Rollback webhook error response:', errorText); + logger.error('Rollback webhook error response', { + status: response.status, + error: errorText.substring(0, 200) + }); throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); } const result = await response.json(); - console.log('Received response from n8n rollback:', JSON.stringify(result, null, 2)); + logger.debug('Received response from n8n rollback', { + success: !result.error, + hasError: !!result.error + }); return { success: true, results: result }; } catch (error) { - console.error('Error triggering n8n rollback workflow:', error); + logger.error('Error triggering n8n rollback workflow', { + error: error instanceof Error ? error.message : String(error) + }); return { success: false, error: error instanceof Error ? error.message : 'Unknown error'