import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { getPublicUrl } from '@/lib/s3'; import { S3_CONFIG } from '@/lib/s3'; import { IntegrationService } from '@/lib/services/integration-service'; import { N8nService } from '@/lib/services/n8n-service'; // Helper function to check authentication async function checkAuth(request: Request, body?: any) { // Check for service account API key first const apiKey = request.headers.get('x-api-key'); console.log('API key from header:', apiKey); console.log('API key from env:', process.env.N8N_API_KEY); console.log('Keys match:', apiKey === process.env.N8N_API_KEY); if (apiKey === process.env.N8N_API_KEY) { // For service account, use the creatorId from the request body if (body?.creatorId) { console.log('Using creatorId from request body:', body.creatorId); // Verify the user exists const user = await prisma.user.findUnique({ where: { id: body.creatorId } }); if (!user) { console.error('Creator user not found:', body.creatorId); return { authorized: false, userId: null }; } return { authorized: true, userId: body.creatorId }; } // Fallback to system user if no creatorId provided console.log('No creatorId provided, using system user'); // Use the first user in the database as system user const systemUser = await prisma.user.findFirst(); if (!systemUser) { console.error('No users found in database for system user fallback'); return { authorized: false, userId: null }; } return { authorized: true, userId: systemUser.id }; } // Fall back to NextAuth session for regular users const session = await getServerSession(authOptions); if (!session?.user?.id) { console.error('Unauthorized access attempt:', { url: request.url, method: request.method, headers: Object.fromEntries(request.headers) }); return { authorized: false, userId: null }; } return { authorized: true, userId: session.user.id }; } // GET endpoint to list missions with filters export async function GET(request: Request) { try { const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { searchParams } = new URL(request.url); const limit = Number(searchParams.get('limit') || '10'); const offset = Number(searchParams.get('offset') || '0'); const search = searchParams.get('search'); // Build query conditions const where: any = {}; // Add search filter if provided if (search) { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, { intention: { contains: search, mode: 'insensitive' } } ]; } // Get missions with basic info const missions = await (prisma as any).mission.findMany({ where, skip: offset, take: limit, orderBy: { createdAt: 'desc' }, select: { id: true, name: true, logo: true, oddScope: true, niveau: true, missionType: true, projection: true, participation: true, services: true, intention: true, createdAt: true, creator: { select: { id: true, email: true } }, missionUsers: { select: { id: true, role: true, user: { select: { id: true, email: true } } } } } }); // Get total count const totalCount = await (prisma as any).mission.count({ where }); // Transform logo paths to public URLs const missionsWithPublicUrls = missions.map((mission: any) => ({ ...mission, logo: mission.logo ? `/api/missions/image/${mission.logo}` : null })); return NextResponse.json({ missions: missionsWithPublicUrls, pagination: { total: totalCount, offset, limit } }); } catch (error) { console.error('Error listing missions:', error); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } } // POST endpoint to create a new mission export async function POST(request: Request) { try { const body = await request.json(); const auth = await checkAuth(request, body); if (!auth.authorized || !auth.userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } // Check for existing mission with the same name const existingMission = await prisma.mission.findFirst({ where: { name: body.name } }); if (existingMission) { return NextResponse.json({ error: 'A mission with this name already exists', existingMission }, { status: 409 }); } // Validate required fields if (!body.name || !body.niveau || !body.intention || !body.missionType || !body.donneurDOrdre || !body.projection) { return NextResponse.json({ error: 'Missing required fields', required: { name: true, niveau: true, intention: true, missionType: true, donneurDOrdre: true, projection: true }, received: { name: !!body.name, niveau: !!body.niveau, intention: !!body.intention, missionType: !!body.missionType, donneurDOrdre: !!body.donneurDOrdre, projection: !!body.projection } }, { status: 400 }); } // Create mission with default values const mission = await prisma.mission.create({ data: { name: body.name, oddScope: body.oddScope || [], niveau: body.niveau || [], intention: body.intention || [], missionType: body.missionType || 'hybrid', donneurDOrdre: body.donneurDOrdre || [], projection: body.projection || [], services: body.services || [], profils: body.profils || [], creatorId: auth.userId } }); // Add guardians if provided if (body.guardians) { const guardianRoles = ['gardien-temps', 'gardien-parole', 'gardien-memoire']; const guardianEntries = Object.entries(body.guardians) .filter(([role, userId]) => guardianRoles.includes(role) && userId) .map(([role, userId]) => ({ role, userId: userId as string, missionId: mission.id })); if (guardianEntries.length > 0) { await prisma.missionUser.createMany({ data: guardianEntries }); } } // Add volunteers if provided if (body.volunteers && body.volunteers.length > 0) { const volunteerEntries = body.volunteers.map((userId: string) => ({ role: 'volontaire', userId, missionId: mission.id })); await prisma.missionUser.createMany({ data: volunteerEntries }); } try { // Trigger the n8n workflow const n8nService = new N8nService(); const workflowResult = await n8nService.createMission({ ...body, missionId: mission.id, creatorId: auth.userId }); // Update mission with integration results, even if some failed if (workflowResult.results) { const updateData: any = {}; // Only update fields that were successfully created if (workflowResult.results.gitRepo?.html_url) { updateData.giteaRepositoryUrl = workflowResult.results.gitRepo.html_url; } if (workflowResult.results.leantimeProject?.result) { updateData.leantimeProjectId = workflowResult.results.leantimeProject.result.toString(); } if (workflowResult.results.rocketChatChannel?.channel?._id) { updateData.rocketChatChannelId = workflowResult.results.rocketChatChannel.channel._id; } if (workflowResult.results.docCollection?.id) { updateData.outlineCollectionId = workflowResult.results.docCollection.id; } // Only update if we have any successful integrations if (Object.keys(updateData).length > 0) { await prisma.mission.update({ where: { id: mission.id }, data: updateData }); } } return NextResponse.json({ success: true, mission: { id: mission.id, name: mission.name, createdAt: mission.createdAt }, workflow: { status: workflowResult.success ? 'success' : 'partial_success', data: workflowResult, errors: workflowResult.errors || [] } }); } catch (workflowError) { // If workflow fails completely, delete the mission and report failure console.error('Workflow error:', workflowError); await prisma.mission.delete({ where: { id: mission.id } }); return NextResponse.json({ error: 'Failed to set up external services', details: workflowError instanceof Error ? workflowError.message : String(workflowError) }, { status: 500 }); } } catch (error) { console.error('Error creating mission:', error); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } }