import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { google } from 'googleapis'; import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // Google Calendar OAuth callback endpoint export async function GET(request: NextRequest) { try { const session = await getServerSession(); const { searchParams } = new URL(request.url); const code = searchParams.get('code'); const state = searchParams.get('state'); // User email passed from start route if (!code) { return NextResponse.redirect(new URL('/tasks?error=oauth_code_missing', request.url)); } // Get user from session or state parameter const userEmail = session?.user?.email || state; if (!userEmail) { return NextResponse.redirect(new URL('/auth/login?error=session_expired', request.url)); } // Find user in database const user = await prisma.user.findUnique({ where: { email: userEmail } }); if (!user) { return NextResponse.redirect(new URL('/auth/login?error=user_not_found', request.url)); } // Initialize Google OAuth client const clientId = process.env.GOOGLE_CLIENT_ID || ''; const clientSecret = process.env.GOOGLE_CLIENT_SECRET || ''; const redirectUri = process.env.GOOGLE_REDIRECT_URI || `${process.env.NEXTAUTH_URL}/api/calendar/google/oauth`; const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri); // Exchange authorization code for access token const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); // Fetch user calendars to store in connection settings // Dynamic import to avoid circular dep issues in some envs, or just standard import? // Standard import is better but we are inside function scope to check diff. // I'll assume the import is added at top level or I force it here if possible. // I will add import at top level in separate chunk if needed? // Replace whole file content is unsafe. I'll use multi-replace. // We need getUserCalendars. I'll use require or assume import added. // Actually, I'll allow ReplaceFileContent to manage imports? No. // I'll use multi_replace to add import AND update logic. // WAIT, better approach: Just implement the fetch logic here locally to avoid import issues or dependency on lib if it changes. // But duplicate code is bad. // I'll add the import at the top. // Logic: const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const response = await calendar.calendarList.list(); const remoteCalendars = response.data.items?.map((item: any) => ({ id: item.id, title: item.summary, isPrimary: item.primary, backgroundColor: item.backgroundColor, // Store calendar color for event fallback selected: true // Default newly found to true })) || []; // Check if connection already exists const existingConnection = await prisma.calendarConnection.findFirst({ where: { userId: user.id, provider: 'google' } }); let finalCalendars = remoteCalendars; if (existingConnection) { // Merge with existing selection if (existingConnection.calendars && Array.isArray(existingConnection.calendars)) { const existingList = existingConnection.calendars as any[]; finalCalendars = remoteCalendars.map(remote => { const match = existingList.find(e => e.id === remote.id); return { ...remote, selected: match ? match.selected : true // Preserve selection }; }); } // Update existing connection await prisma.calendarConnection.update({ where: { id: existingConnection.id }, data: { accessToken: tokens.access_token || '', refreshToken: tokens.refresh_token || existingConnection.refreshToken, expiresAt: tokens.expiry_date ? new Date(tokens.expiry_date) : null, calendars: finalCalendars, // Store calendars updatedAt: new Date() } }); } else { // Create new connection await prisma.calendarConnection.create({ data: { userId: user.id, provider: 'google', accessToken: tokens.access_token || '', refreshToken: tokens.refresh_token || null, expiresAt: tokens.expiry_date ? new Date(tokens.expiry_date) : null, calendars: finalCalendars, // Store calendars } }); } // Redirect to tasks page with success message return NextResponse.redirect(new URL('/tasks?calendar=connected', request.url)); } catch (error) { console.error('Google OAuth error:', error); return NextResponse.redirect(new URL('/tasks?error=oauth_failed', request.url)); } }