import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; import { getCachedTasksData, cacheTasksData } from "@/lib/redis"; interface Task { id: string; headline: string; projectName: string; projectId: number; status: number; dateToFinish: string | null; milestone: string | null; details: string | null; createdOn: string; editedOn: string | null; editorId: string; editorFirstname: string; editorLastname: string; } async function getLeantimeUserId(email: string): Promise { try { if (!process.env.LEANTIME_TOKEN) { console.error('LEANTIME_TOKEN is not set in environment variables'); return null; } console.log('Fetching Leantime users for email:', email); console.log('API URL:', process.env.LEANTIME_API_URL); console.log('Token length:', process.env.LEANTIME_TOKEN.length); const headers: Record = { 'Content-Type': 'application/json', 'X-API-Key': process.env.LEANTIME_TOKEN }; const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { method: 'POST', headers, body: JSON.stringify({ jsonrpc: '2.0', method: 'leantime.rpc.users.getAll', id: 1 }), }); const responseText = await response.text(); console.log('Raw Leantime response:', responseText); if (!response.ok) { console.error('Failed to fetch Leantime users:', { status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()) }); return null; } let data; try { data = JSON.parse(responseText); } catch (e) { console.error('Failed to parse Leantime response:', e); return null; } console.log('Leantime users response:', data); if (!data.result || !Array.isArray(data.result)) { console.error('Invalid response format from Leantime users API'); return null; } const users = data.result; const user = users.find((u: any) => u.username === email); if (user) { console.log('Found Leantime user:', { id: user.id, username: user.username }); } else { console.log('No Leantime user found for username:', email); } return user ? user.id : null; } catch (error) { console.error('Error fetching Leantime user ID:', error); return null; } } export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.email) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Check for force refresh parameter const url = new URL(request.url); const forceRefresh = url.searchParams.get('refresh') === 'true'; // Try to get data from cache if not forcing refresh if (!forceRefresh) { const cachedTasks = await getCachedTasksData(session.user.id); if (cachedTasks) { console.log(`Using cached tasks data for user ${session.user.id}`); return NextResponse.json(cachedTasks); } } console.log('Fetching tasks for user:', session.user.email); const userId = await getLeantimeUserId(session.user.email); if (!userId) { console.error('User not found in Leantime'); return NextResponse.json({ error: "User not found in Leantime" }, { status: 404 }); } console.log('Fetching tasks for Leantime user ID:', userId); const headers: Record = { 'Content-Type': 'application/json', 'X-API-Key': process.env.LEANTIME_TOKEN! }; const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { method: 'POST', headers, body: JSON.stringify({ jsonrpc: '2.0', method: 'leantime.rpc.tickets.getAll', params: { userId: userId, status: "all" }, id: 1 }), }); const responseText = await response.text(); console.log('Tasks API response status:', response.status); if (!response.ok) { console.error('Failed to fetch tasks from Leantime:', { status: response.status, statusText: response.statusText }); throw new Error('Failed to fetch tasks from Leantime'); } let data; try { data = JSON.parse(responseText); } catch (e) { console.error('Failed to parse tasks response'); throw new Error('Invalid response format from Leantime'); } if (!data.result || !Array.isArray(data.result)) { console.error('Invalid response format from Leantime tasks API'); throw new Error('Invalid response format from Leantime'); } // Log only the number of tasks and their IDs console.log('Received tasks count:', data.result.length); console.log('Task IDs:', data.result.map((task: any) => task.id)); const tasks = data.result .filter((task: any) => { // Log raw task data for debugging console.log('Raw task data:', { id: task.id, headline: task.headline, status: task.status, type: task.type, dependingTicketId: task.dependingTicketId }); // Filter out any task (main or subtask) that has status Done (5) if (task.status === 5) { console.log(`Filtering out Done task ${task.id} (type: ${task.type || 'main'}, status: ${task.status})`); return false; } // Convert both to strings for comparison to handle any type mismatches const taskEditorId = String(task.editorId).trim(); const currentUserId = String(userId).trim(); // Only show tasks where the user is the editor const isUserEditor = taskEditorId === currentUserId; console.log(`Task ${task.id}: status=${task.status}, type=${task.type || 'main'}, parentId=${task.dependingTicketId || 'none'}, isUserEditor=${isUserEditor}`); return isUserEditor; }) .map((task: any) => ({ id: task.id.toString(), headline: task.headline, projectName: task.projectName, projectId: task.projectId, status: task.status, dateToFinish: task.dateToFinish || null, milestone: task.type || null, details: task.description || null, createdOn: task.dateCreated, editedOn: task.editedOn || null, editorId: task.editorId, editorFirstname: task.editorFirstname, editorLastname: task.editorLastname, type: task.type || null, // Added type field to identify subtasks dependingTicketId: task.dependingTicketId || null // Added parent task reference })); console.log(`Found ${tasks.length} tasks assigned to user ${userId}`); // Cache the results await cacheTasksData(session.user.id, tasks); return NextResponse.json(tasks); } catch (error) { console.error('Error in tasks route:', error); return NextResponse.json( { error: "Failed to fetch tasks" }, { status: 500 } ); } }