Refactir logging mission deep
This commit is contained in:
parent
fbb8eac162
commit
dce71a2cb3
@ -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 }
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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<boolean> {
|
||||
}));
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<void> {
|
||||
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<void> {
|
||||
|
||||
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<void> {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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<any> {
|
||||
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<any> {
|
||||
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'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user