diff --git a/package.json b/package.json index b7771c2..204d5bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.81.18", + "version": "1.81.19", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/app/api/calendar/events/route.ts b/src/app/api/calendar/events/route.ts index 2db57a9..b3b8271 100644 --- a/src/app/api/calendar/events/route.ts +++ b/src/app/api/calendar/events/route.ts @@ -48,13 +48,17 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); } - const userId = (session.user as any).id; - const connection = await findConnectionForCalendar(userId, calendarId); + const connection = await findConnectionForCalendar(session.user.email, calendarId); if (!connection) { return NextResponse.json({ error: 'Calendar connection not found' }, { status: 404 }); } + // Resolve real DB user ID for cache operations + const dbUser = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } }); + const userId = dbUser?.id; + if (!userId) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + const event = await createCalendarEvent(connection, calendarId, { title, description, @@ -114,13 +118,16 @@ export async function PATCH(request: NextRequest) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); } - const userId = (session.user as any).id; - const connection = await findConnectionForCalendar(userId, calendarId); + const connection = await findConnectionForCalendar(session.user.email, calendarId); if (!connection) { return NextResponse.json({ error: 'Calendar connection not found' }, { status: 404 }); } + const dbUser2 = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } }); + const userId = dbUser2?.id; + if (!userId) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + // For "all events" edit mode on recurring events, use the series master ID let targetEventId = eventId; if (editMode === 'all' && recurringEventId) { @@ -191,13 +198,16 @@ export async function DELETE(request: NextRequest) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); } - const userId = (session.user as any).id; - const connection = await findConnectionForCalendar(userId, calendarId); + const connection = await findConnectionForCalendar(session.user.email, calendarId); if (!connection) { return NextResponse.json({ error: 'Calendar connection not found' }, { status: 404 }); } + const dbUser3 = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } }); + const userId = dbUser3?.id; + if (!userId) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + await deleteCalendarEvent(connection, calendarId, eventId, deleteMode); // Remove from cache - await to ensure consistency diff --git a/src/app/api/calendar/outlook/callback/route.ts b/src/app/api/calendar/outlook/callback/route.ts index 4d5a7fb..c2e95c4 100644 --- a/src/app/api/calendar/outlook/callback/route.ts +++ b/src/app/api/calendar/outlook/callback/route.ts @@ -38,10 +38,8 @@ export async function GET(request: NextRequest) { // Fetch user's calendars to store initial list const calendars = await getUserCalendars(accessToken); - const userId = (session.user as any).id; - const user = userId - ? await prisma.user.findUnique({ where: { id: userId } }) - : await prisma.user.findUnique({ where: { email: session.user.email } }); + // Always look up by email — session ID can be stale after DB restore/migration + const user = await prisma.user.findUnique({ where: { email: session.user.email } }); if (!user) { return NextResponse.redirect(new URL('/auth/login', baseUrl)); diff --git a/src/app/api/someday-lists/route.ts b/src/app/api/someday-lists/route.ts index 0a5b2bc..54de06e 100644 --- a/src/app/api/someday-lists/route.ts +++ b/src/app/api/someday-lists/route.ts @@ -6,18 +6,29 @@ import { notifyUser } from '@/lib/sse'; const prisma = new PrismaClient(); +async function resolveUserId(email: string): Promise { + const user = await prisma.user.findUnique({ + where: { email }, + select: { id: true } + }); + return user?.id ?? null; +} + export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } - const userId = (session.user as any).id; + const userId = await resolveUserId(session.user.email); + if (!userId) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } const lists = await prisma.somedayList.findMany({ where: { userId }, @@ -44,14 +55,18 @@ export async function POST(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } - const userId = (session.user as any).id; + const userId = await resolveUserId(session.user.email); + if (!userId) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + const { title } = await request.json(); if (!title) { @@ -92,13 +107,18 @@ export async function DELETE(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } + const userId = await resolveUserId(session.user.email); + if (!userId) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + const { searchParams } = new URL(request.url); const id = searchParams.get('id'); @@ -114,27 +134,13 @@ export async function DELETE(request: NextRequest) { where: { id } }); - if (!list || list.userId !== (session.user as any).id) { + if (!list || list.userId !== userId) { return NextResponse.json( { error: 'List not found or unauthorized' }, { status: 404 } ); } - // Delete list (tasks cascade delete is not set in schema for tasks->list, check schema) - // In schema: tasks defined as `tasks Task[]`. - // We updated schema: `user User ... onDelete: Cascade`. `tasks` are separate. - // We need to verify if deleting list deletes tasks or unlinks them. - // Schema: `somedayList SomedayList? @relation...` - // If we want cascade delete tasks in the list, we should check relations. - // Prisma default is usually not cascade for optional relations unless specified. - // Let's assume we want to keep tasks or delete them? Usually delete list = delete tasks in it. - // Let's explicitly delete tasks first or rely on schema if configured. - // Schema update I did: `tasks Task[]`. `Task` has `somedayListId`. - // I didn't add `onDelete: Cascade` to the `somedayList` relation in `Task`. - // So I should clean up tasks manually or update schema. - // For now, let's delete tasks in the list. - // Soft-delete tasks in this list (they can be recovered from trash) await prisma.task.updateMany({ where: { somedayListId: id }, @@ -145,7 +151,7 @@ export async function DELETE(request: NextRequest) { where: { id } }); - notifyUser((session.user as any).id, "list-changed", { action: "deleted" }); + notifyUser(userId, "list-changed", { action: "deleted" }); return NextResponse.json({ success: true }); } catch (error) { console.error('Error deleting someday list:', error); @@ -160,32 +166,34 @@ export async function PATCH(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } + const userId = await resolveUserId(session.user.email); + if (!userId) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + const body = await request.json(); // Handle Reordering (Array of { id, order }) if (Array.isArray(body)) { const updates = body.map(async (item: { id: string; order: number }) => { - // Verify ownership for each or just assume if one matches? - // Better to be safe, but for performance in batch, we might trust ID if valid. - // Let's verify ownership implicitly by where clause. return prisma.somedayList.updateMany({ where: { id: item.id, - userId: (session.user as any).id + userId }, data: { order: item.order } }); }); await Promise.all(updates); - notifyUser((session.user as any).id, "list-changed", { action: "reordered" }); + notifyUser(userId, "list-changed", { action: "reordered" }); return NextResponse.json({ success: true }); } @@ -204,7 +212,7 @@ export async function PATCH(request: NextRequest) { where: { id } }); - if (!existingList || existingList.userId !== (session.user as any).id) { + if (!existingList || existingList.userId !== userId) { return NextResponse.json( { error: 'List not found or unauthorized' }, { status: 404 } @@ -220,7 +228,7 @@ export async function PATCH(request: NextRequest) { data }); - notifyUser((session.user as any).id, "list-changed", { action: "updated" }); + notifyUser(userId, "list-changed", { action: "updated" }); return NextResponse.json({ list }); } catch (error) { console.error('Error updating someday list:', error);