import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { N8nService } from '@/lib/services/n8n-service'; import { Prisma } from '@prisma/client'; // Types interface MissionCreateInput { name: string; oddScope: string[]; niveau?: string; intention?: string; missionType?: string; donneurDOrdre?: string; projection?: string; services?: string[]; participation?: string; profils?: string[]; guardians?: Record; volunteers?: string[]; creatorId?: string; logo?: { data: string; name?: string; type?: string; } | null; attachments?: Array<{ data: string; name?: string; type?: string; }>; leantimeProjectId?: string | null; outlineCollectionId?: string | null; rocketChatChannelId?: string | null; giteaRepositoryUrl?: string | null; penpotProjectId?: string | null; } interface MissionUserInput { role: string; userId: string; missionId: string; } interface MissionResponse { id: string; name: string; oddScope: string[]; niveau: string; intention: string; missionType: string; donneurDOrdre: string; projection: string; services: string[]; profils: string[]; participation: string; creatorId: string; logo: string | null; leantimeProjectId: string | null; outlineCollectionId: string | null; rocketChatChannelId: string | null; giteaRepositoryUrl: string | null; penpotProjectId: string | null; createdAt: Date; updatedAt: Date; } // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); return { authorized: !!session?.user, userId: session?.user?.id }; } // GET endpoint to list missions 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'); const name = searchParams.get('name'); const where: Prisma.MissionWhereInput = {}; if (search) { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, { intention: { contains: search, mode: 'insensitive' } } ]; } if (name) { where.name = name; } const missions = await prisma.mission.findMany({ where, skip: offset, take: limit, orderBy: { createdAt: 'desc' }, include: { creator: { select: { id: true, email: true } }, missionUsers: { include: { user: { select: { id: true, email: true } } } } } }); const totalCount = await prisma.mission.count({ where }); return NextResponse.json({ missions, 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 { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const body = await request.json() as MissionCreateInput; // Validate required fields const requiredFields = ['name', 'oddScope']; const missingFields = requiredFields.filter(field => !body[field as keyof MissionCreateInput]); if (missingFields.length > 0) { return NextResponse.json({ error: 'Missing required fields', missingFields }, { status: 400 }); } // Check if this is a request from n8n const isN8nRequest = request.headers.get('x-api-key') === process.env.N8N_API_KEY; // Get the creator ID from the appropriate source const creatorId = isN8nRequest ? body.creatorId : userId; // Verify creator exists const creator = await prisma.user.findUnique({ where: { id: creatorId } }); if (!creator) { return NextResponse.json({ error: 'Invalid creator ID', details: 'The specified creator does not exist', code: 'INVALID_CREATOR' }, { status: 400 }); } // Check if mission with same name exists const existingMission = await prisma.mission.findFirst({ where: { name: body.name } }); if (existingMission) { // Update existing mission with new integration IDs const updatedMission = await prisma.mission.update({ where: { id: existingMission.id }, data: { leantimeProjectId: body.leantimeProjectId ? String(body.leantimeProjectId) : null, outlineCollectionId: body.outlineCollectionId || null, rocketChatChannelId: body.rocketChatChannelId || null, giteaRepositoryUrl: body.giteaRepositoryUrl || null, penpotProjectId: body.penpotProjectId || null } as Prisma.MissionUpdateInput }); return NextResponse.json({ message: 'Mission updated successfully', mission: updatedMission, isUpdate: true }); } // Handle n8n workflow if (!isN8nRequest) { const n8nService = new N8nService(); // Prepare the data for n8n const n8nData = { name: body.name, oddScope: Array.isArray(body.oddScope) ? body.oddScope : [body.oddScope], niveau: body.niveau || 'default', intention: body.intention?.trim() || '', missionType: body.missionType || 'default', donneurDOrdre: body.donneurDOrdre || 'default', projection: body.projection || 'default', services: Array.isArray(body.services) ? body.services : [], participation: body.participation || 'default', profils: Array.isArray(body.profils) ? body.profils : [], guardians: body.guardians || {}, volunteers: Array.isArray(body.volunteers) ? body.volunteers : [], creatorId: userId, logo: body.logo ? { data: body.logo.data, name: body.logo.name || 'logo.png', type: body.logo.type || 'image/png' } : null, attachments: body.attachments || [], config: { N8N_API_KEY: process.env.N8N_API_KEY, MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL || 'https://api.slm-lab.net/api' } }; try { const workflowResult = await n8nService.triggerMissionCreation(n8nData); if (!workflowResult.success) { console.error('n8n workflow failed:', workflowResult.error); return NextResponse.json({ error: 'Failed to create mission resources', details: workflowResult.error || 'The mission creation process failed', code: 'WORKFLOW_ERROR' }, { status: 500 }); } return NextResponse.json({ success: true, status: 'success', message: 'Mission creation initiated', mission: { name: n8nData.name, oddScope: n8nData.oddScope, niveau: n8nData.niveau, intention: n8nData.intention, missionType: n8nData.missionType, donneurDOrdre: n8nData.donneurDOrdre, projection: n8nData.projection, services: n8nData.services, profils: n8nData.profils, participation: n8nData.participation, creatorId: n8nData.creatorId } }); } catch (error) { console.error('Error triggering n8n workflow:', error); return NextResponse.json({ error: 'Failed to initiate mission creation', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } } // Create mission directly (n8n request) try { const mission = await prisma.mission.create({ data: { name: body.name, oddScope: body.oddScope || ['default'], niveau: body.niveau, intention: body.intention, missionType: body.missionType, donneurDOrdre: body.donneurDOrdre, projection: body.projection, services: Array.isArray(body.services) ? body.services.filter(Boolean) : [], profils: Array.isArray(body.profils) ? body.profils.filter(Boolean) : [], participation: body.participation || 'default', creatorId: creatorId, logo: body.logo || null, leantimeProjectId: body.leantimeProjectId ? String(body.leantimeProjectId) : null, outlineCollectionId: body.outlineCollectionId || null, rocketChatChannelId: body.rocketChatChannelId || null, giteaRepositoryUrl: body.giteaRepositoryUrl || null, penpotProjectId: body.penpotProjectId || null } as Prisma.MissionUncheckedCreateInput }); // Add guardians and volunteers if (body.guardians || body.volunteers) { const missionUsers: MissionUserInput[] = []; // Add guardians if (body.guardians) { Object.entries(body.guardians).forEach(([role, userId]) => { if (userId) { missionUsers.push({ role, userId: userId as string, missionId: mission.id }); } }); } // Add volunteers if (body.volunteers && Array.isArray(body.volunteers)) { body.volunteers.forEach(userId => { if (userId) { missionUsers.push({ role: 'volontaire', userId, missionId: mission.id }); } }); } if (missionUsers.length > 0) { await prisma.missionUser.createMany({ data: missionUsers }); } } return NextResponse.json({ success: true, status: 'success', message: 'Mission created successfully', mission: { id: (mission as any).id, name: (mission as any).name, oddScope: (mission as any).oddScope, niveau: (mission as any).niveau, intention: (mission as any).intention, missionType: (mission as any).missionType, donneurDOrdre: (mission as any).donneurDOrdre, projection: (mission as any).projection, services: (mission as any).services, profils: (mission as any).profils, participation: (mission as any).participation, creatorId: (mission as any).creatorId, logo: (mission as any).logo, leantimeProjectId: (mission as any).leantimeProjectId, outlineCollectionId: (mission as any).outlineCollectionId, rocketChatChannelId: (mission as any).rocketChatChannelId, giteaRepositoryUrl: (mission as any).giteaRepositoryUrl, penpotProjectId: (mission as any).penpotProjectId, createdAt: (mission as any).createdAt, updatedAt: (mission as any).updatedAt } as MissionResponse }); } catch (error) { console.error('Error creating mission:', error); if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error.code === 'P2003') { return NextResponse.json({ success: false, status: 'error', error: 'Invalid reference', message: 'One or more referenced users do not exist', code: 'INVALID_REFERENCE' }, { status: 400 }); } } return NextResponse.json( { success: false, status: 'error', error: 'Failed to create mission', message: error instanceof Error ? error.message : String(error) }, { status: 500 } ); } } catch (error) { console.error('Error creating mission:', error); return NextResponse.json( { error: 'Failed to create mission', details: error instanceof Error ? error.message : String(error) }, { status: 500 } ); } }