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