My-Weekly-ToDo-List/src/app/api/user/export-data/route.ts
mARTin 6574eeb65f feat: add backup/restore, task indicators, and multiple bug fixes
- Add JSON export/import for all tasks, anyday lists, and projects (Settings > Account > Backup & Restore)
- Add merge and replace import modes with confirmation for destructive replace
- Add resizable notes sidebar with drag handle on left edge
- Add subtask count indicator (indigo pill) on tasks that toggles subtask list
- Add note indicator (amber pill) on tasks that toggles inline notes
- Show Account ID in Settings > Account as read-only identifier
- Fix goal save bug: saved goals matching default text are no longer ignored
- Fix calendar week number using Monday within visible range instead of middle day
- Fix calendar events not appearing instantly by delaying sync refresh 3s
- Grey out URL field when creating events on Google/Outlook calendars

v1.12.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:39:37 +01:00

123 lines
3.8 KiB
TypeScript

import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from "@/lib/auth";
import { prisma } from '@/lib/prisma';
export async function GET() {
const session = await getServerSession(authOptions);
if (!session || !session.user?.email) {
return new NextResponse('Unauthorized', { status: 401 });
}
try {
const user = await prisma.user.findUnique({
where: { email: session.user.email },
});
if (!user) {
return new NextResponse('User not found', { status: 404 });
}
// Fetch someday lists
const somedayLists = await prisma.somedayList.findMany({
where: { userId: user.id },
orderBy: { order: 'asc' },
select: {
id: true,
title: true,
order: true,
createdAt: true,
updatedAt: true,
},
});
// Fetch projects
const projects = await prisma.project.findMany({
where: { userId: user.id },
orderBy: { order: 'asc' },
select: {
id: true,
name: true,
icon: true,
color: true,
description: true,
order: true,
createdAt: true,
updatedAt: true,
},
});
// Fetch all non-deleted tasks (top-level and subtasks)
const allTasks = await prisma.task.findMany({
where: {
userId: user.id,
deletedAt: null,
},
orderBy: { order: 'asc' },
select: {
id: true,
title: true,
description: true,
markdownContent: true,
completed: true,
isRolling: true,
order: true,
dayOfWeek: true,
scheduledDate: true,
somedayListId: true,
originalDate: true,
startTime: true,
endTime: true,
isRecurring: true,
recurrenceInterval: true,
recurrenceUnit: true,
recurrenceTime: true,
recurrenceEndDate: true,
createdAt: true,
updatedAt: true,
parentTaskId: true,
somedaySlotIndex: true,
projectId: true,
},
});
// Build a tree: nest subtasks under their parents
const taskMap = new Map<string, any>();
const topLevelTasks: any[] = [];
for (const task of allTasks) {
taskMap.set(task.id, { ...task, subTasks: [] });
}
for (const task of allTasks) {
const taskWithSubs = taskMap.get(task.id)!;
if (task.parentTaskId && taskMap.has(task.parentTaskId)) {
taskMap.get(task.parentTaskId)!.subTasks.push(taskWithSubs);
} else {
topLevelTasks.push(taskWithSubs);
}
}
const exportData = {
exportVersion: 1,
exportDate: new Date().toISOString(),
somedayLists,
projects,
tasks: topLevelTasks,
};
const jsonContent = JSON.stringify(exportData, null, 2);
return new NextResponse(jsonContent, {
headers: {
'Content-Type': 'application/json',
'Content-Disposition': `attachment; filename="weekly_todo_backup_${new Date().toISOString().split('T')[0]}.json"`,
},
});
} catch (error) {
console.error('Export data error:', error);
return new NextResponse('Internal Server Error', { status: 500 });
}
}