632 lines
30 KiB
TypeScript
632 lines
30 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from "@/lib/auth";
|
|
import { prisma } from '@/lib/prisma';
|
|
import { createGoogleClient, createGoogleTask, updateGoogleTaskStatus, updateGoogleTask, deleteGoogleTask, fetchGoogleTasksForSync, GoogleTask } from '@/lib/google-tasks';
|
|
import { fetchMsTodoTasksForSync, updateMsTodoTask, deleteMsTodoTask, createMsTodoTask, isMsTodoTaskCompleted } from '@/lib/microsoft-todo';
|
|
import { getOutlookAccessToken } from '@/lib/outlook-token';
|
|
|
|
// GET - Pull changes from Google Tasks into local DB
|
|
export async function GET(req: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
if (!session?.user?.email) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const user = await prisma.user.findUnique({
|
|
where: { email: session.user.email }
|
|
});
|
|
if (!user) {
|
|
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
|
}
|
|
|
|
// Find all local tasks linked to external providers
|
|
const allExternalTasks = await prisma.task.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
externalProvider: { in: ['google', 'outlook'] },
|
|
externalId: { not: null },
|
|
deletedAt: null,
|
|
}
|
|
});
|
|
|
|
// Find all synced SomedayLists (for discovering new remote tasks)
|
|
const syncedLists = await prisma.somedayList.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
externalId: { not: null },
|
|
externalProvider: { not: null },
|
|
}
|
|
});
|
|
|
|
const googleLocalTasks = allExternalTasks.filter(t => t.externalProvider === 'google');
|
|
const outlookLocalTasks = allExternalTasks.filter(t => t.externalProvider === 'outlook');
|
|
|
|
let updated = 0;
|
|
let deleted = 0;
|
|
let created = 0;
|
|
|
|
// --- Google Tasks pull-sync ---
|
|
const googleSyncedLists = syncedLists.filter(l => l.externalProvider === 'google');
|
|
const hasGoogleTasks = googleLocalTasks.length > 0 || googleSyncedLists.length > 0;
|
|
|
|
if (hasGoogleTasks) {
|
|
const account = await prisma.account.findFirst({
|
|
where: { userId: user.id, provider: { in: ['google-calendar', 'google'] } }
|
|
});
|
|
|
|
if (account?.access_token) {
|
|
const client = createGoogleClient(account.access_token, account.refresh_token || undefined);
|
|
|
|
// Build set of all Google list IDs to sync (from tasks + synced lists)
|
|
const googleListIds = new Set<string>();
|
|
const listIdToSomedayList = new Map<string, { id: string; title: string }>();
|
|
|
|
for (const task of googleLocalTasks) {
|
|
if (task.externalListId) googleListIds.add(task.externalListId);
|
|
}
|
|
for (const sl of googleSyncedLists) {
|
|
if (sl.externalId) {
|
|
googleListIds.add(sl.externalId);
|
|
listIdToSomedayList.set(sl.externalId, { id: sl.id, title: sl.title });
|
|
}
|
|
}
|
|
|
|
// Group existing local tasks by list
|
|
const googleByList = new Map<string, typeof googleLocalTasks>();
|
|
for (const task of googleLocalTasks) {
|
|
if (!task.externalListId) continue;
|
|
if (!googleByList.has(task.externalListId)) {
|
|
googleByList.set(task.externalListId, []);
|
|
}
|
|
googleByList.get(task.externalListId)!.push(task);
|
|
}
|
|
|
|
for (const listId of googleListIds) {
|
|
const localTasks = googleByList.get(listId) || [];
|
|
try {
|
|
const remoteTasks = await fetchGoogleTasksForSync(client, listId);
|
|
const remoteMap = new Map(remoteTasks.map(t => [t.id, t]));
|
|
|
|
// Build externalId -> localId map for parent linking
|
|
const extToLocalMap = new Map<string, string>();
|
|
for (const t of localTasks) {
|
|
if (t.externalId) extToLocalMap.set(t.externalId, t.id);
|
|
}
|
|
|
|
// Build set of existing external IDs for quick lookup
|
|
const existingExternalIds = new Set(localTasks.map(t => t.externalId));
|
|
|
|
// Update/delete existing local tasks
|
|
for (const localTask of localTasks) {
|
|
const remote = remoteMap.get(localTask.externalId!);
|
|
|
|
if (!remote) {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: { deletedAt: new Date() }
|
|
});
|
|
deleted++;
|
|
continue;
|
|
}
|
|
|
|
const remoteUpdated = new Date(remote.updated);
|
|
const localUpdated = localTask.lastSyncedAt || localTask.updatedAt;
|
|
if (remoteUpdated <= localUpdated) continue;
|
|
|
|
const updateData: any = { lastSyncedAt: new Date() };
|
|
|
|
const remoteCompleted = remote.status === 'completed';
|
|
if (remoteCompleted !== localTask.completed) {
|
|
updateData.completed = remoteCompleted;
|
|
}
|
|
|
|
if (remote.title && remote.title !== localTask.title) {
|
|
updateData.title = remote.title;
|
|
}
|
|
|
|
if (remote.notes !== undefined && remote.notes !== (localTask.description || undefined)) {
|
|
updateData.description = remote.notes || null;
|
|
}
|
|
|
|
// Sync parent relationship from Google Tasks
|
|
if (remote.parent) {
|
|
const parentLocalId = extToLocalMap.get(remote.parent);
|
|
if (parentLocalId && localTask.parentTaskId !== parentLocalId) {
|
|
updateData.parentTaskId = parentLocalId;
|
|
}
|
|
} else if (localTask.parentTaskId && !remote.parent) {
|
|
updateData.parentTaskId = null;
|
|
}
|
|
|
|
if (Object.keys(updateData).length > 1) {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: updateData
|
|
});
|
|
updated++;
|
|
} else {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: { lastSyncedAt: new Date() }
|
|
});
|
|
}
|
|
}
|
|
|
|
// Create new local tasks for remote tasks not yet in local DB
|
|
const somedayListInfo = listIdToSomedayList.get(listId);
|
|
if (somedayListInfo) {
|
|
// Sort: parents first, then children
|
|
const newRemoteTasks = remoteTasks
|
|
.filter(rt => !existingExternalIds.has(rt.id))
|
|
.sort((a, b) => {
|
|
if (!a.parent && b.parent) return -1;
|
|
if (a.parent && !b.parent) return 1;
|
|
return 0;
|
|
});
|
|
|
|
for (const remote of newRemoteTasks) {
|
|
// Skip empty-title tasks
|
|
if (!remote.title || !remote.title.trim()) continue;
|
|
|
|
const parentLocalId = remote.parent ? extToLocalMap.get(remote.parent) : undefined;
|
|
|
|
const newTask = await prisma.task.create({
|
|
data: {
|
|
userId: user.id,
|
|
title: remote.title,
|
|
description: remote.notes || null,
|
|
completed: remote.status === 'completed',
|
|
somedayListId: somedayListInfo.id,
|
|
externalId: remote.id,
|
|
externalProvider: 'google',
|
|
externalListId: listId,
|
|
parentTaskId: parentLocalId || null,
|
|
lastSyncedAt: new Date(),
|
|
}
|
|
});
|
|
extToLocalMap.set(remote.id, newTask.id);
|
|
existingExternalIds.add(remote.id);
|
|
created++;
|
|
}
|
|
}
|
|
} catch (listError) {
|
|
console.error(`Error syncing Google list ${listId}:`, listError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Microsoft To-Do pull-sync ---
|
|
const outlookSyncedLists = syncedLists.filter(l => l.externalProvider === 'outlook');
|
|
const hasOutlookTasks = outlookLocalTasks.length > 0 || outlookSyncedLists.length > 0;
|
|
|
|
if (hasOutlookTasks) {
|
|
const outlookToken = await getOutlookAccessToken(user.id);
|
|
|
|
if (outlookToken) {
|
|
// Build set of all Outlook list IDs to sync
|
|
const outlookListIds = new Set<string>();
|
|
const outlookListIdToSomedayList = new Map<string, { id: string; title: string }>();
|
|
|
|
for (const task of outlookLocalTasks) {
|
|
if (task.externalListId) outlookListIds.add(task.externalListId);
|
|
}
|
|
for (const sl of outlookSyncedLists) {
|
|
if (sl.externalId) {
|
|
outlookListIds.add(sl.externalId);
|
|
outlookListIdToSomedayList.set(sl.externalId, { id: sl.id, title: sl.title });
|
|
}
|
|
}
|
|
|
|
// Group existing local tasks by list
|
|
const outlookByList = new Map<string, typeof outlookLocalTasks>();
|
|
for (const task of outlookLocalTasks) {
|
|
if (!task.externalListId) continue;
|
|
if (!outlookByList.has(task.externalListId)) {
|
|
outlookByList.set(task.externalListId, []);
|
|
}
|
|
outlookByList.get(task.externalListId)!.push(task);
|
|
}
|
|
|
|
for (const listId of outlookListIds) {
|
|
const localTasks = outlookByList.get(listId) || [];
|
|
try {
|
|
const remoteTasks = await fetchMsTodoTasksForSync(outlookToken, listId);
|
|
const remoteMap = new Map(remoteTasks.map(t => [t.id, t]));
|
|
|
|
const existingExternalIds = new Set(localTasks.map(t => t.externalId));
|
|
|
|
for (const localTask of localTasks) {
|
|
const remote = remoteMap.get(localTask.externalId!);
|
|
|
|
if (!remote) {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: { deletedAt: new Date() }
|
|
});
|
|
deleted++;
|
|
continue;
|
|
}
|
|
|
|
const remoteUpdated = new Date(remote.lastModifiedDateTime);
|
|
const localUpdated = localTask.lastSyncedAt || localTask.updatedAt;
|
|
if (remoteUpdated <= localUpdated) continue;
|
|
|
|
const updateData: any = { lastSyncedAt: new Date() };
|
|
|
|
const remoteCompleted = isMsTodoTaskCompleted(remote.status);
|
|
if (remoteCompleted !== localTask.completed) {
|
|
updateData.completed = remoteCompleted;
|
|
}
|
|
|
|
if (remote.title && remote.title !== localTask.title) {
|
|
updateData.title = remote.title;
|
|
}
|
|
|
|
const remoteNotes = remote.body?.content || null;
|
|
if (remoteNotes !== (localTask.description || null)) {
|
|
updateData.description = remoteNotes;
|
|
}
|
|
|
|
if (Object.keys(updateData).length > 1) {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: updateData
|
|
});
|
|
updated++;
|
|
} else {
|
|
await prisma.task.update({
|
|
where: { id: localTask.id },
|
|
data: { lastSyncedAt: new Date() }
|
|
});
|
|
}
|
|
}
|
|
|
|
// Create new local tasks for remote tasks not yet in local DB
|
|
const somedayListInfo = outlookListIdToSomedayList.get(listId);
|
|
if (somedayListInfo) {
|
|
const newRemoteTasks = remoteTasks.filter(rt => !existingExternalIds.has(rt.id));
|
|
|
|
for (const remote of newRemoteTasks) {
|
|
if (!remote.title || !remote.title.trim()) continue;
|
|
|
|
await prisma.task.create({
|
|
data: {
|
|
userId: user.id,
|
|
title: remote.title,
|
|
description: remote.body?.content || null,
|
|
completed: isMsTodoTaskCompleted(remote.status),
|
|
somedayListId: somedayListInfo.id,
|
|
externalId: remote.id,
|
|
externalProvider: 'outlook',
|
|
externalListId: listId,
|
|
lastSyncedAt: new Date(),
|
|
}
|
|
});
|
|
existingExternalIds.add(remote.id);
|
|
created++;
|
|
}
|
|
}
|
|
} catch (listError) {
|
|
console.error(`Error syncing Outlook list ${listId}:`, listError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Synology pull-sync ---
|
|
const synologySyncedLists = syncedLists.filter(l => l.externalProvider === 'synology');
|
|
const synologyLocalTasks = allExternalTasks.filter(t => t.externalProvider === 'synology');
|
|
const hasSynologyTasks = synologyLocalTasks.length > 0 || synologySyncedLists.length > 0;
|
|
|
|
if (hasSynologyTasks) {
|
|
const synoConnection = await prisma.calendarConnection.findFirst({
|
|
where: { userId: user.id, provider: 'synology' }
|
|
});
|
|
|
|
if (synoConnection?.accessToken && synoConnection?.refreshToken) {
|
|
const [synoUsername, synoPassword] = synoConnection.accessToken.split(':');
|
|
const synoServerUrl = synoConnection.refreshToken;
|
|
|
|
if (synoUsername && synoPassword && synoServerUrl) {
|
|
const { fetchSynologyTasks } = await import('@/lib/synology-tasks');
|
|
|
|
const synoListIds = new Set<string>();
|
|
const synoListIdToSomedayList = new Map<string, { id: string; title: string }>();
|
|
|
|
for (const task of synologyLocalTasks) {
|
|
if (task.externalListId) synoListIds.add(task.externalListId);
|
|
}
|
|
for (const sl of synologySyncedLists) {
|
|
if (sl.externalId) {
|
|
synoListIds.add(sl.externalId);
|
|
synoListIdToSomedayList.set(sl.externalId, { id: sl.id, title: sl.title });
|
|
}
|
|
}
|
|
|
|
const synoByList = new Map<string, typeof synologyLocalTasks>();
|
|
for (const task of synologyLocalTasks) {
|
|
if (!task.externalListId) continue;
|
|
if (!synoByList.has(task.externalListId)) synoByList.set(task.externalListId, []);
|
|
synoByList.get(task.externalListId)!.push(task);
|
|
}
|
|
|
|
for (const listId of synoListIds) {
|
|
const localTasks = synoByList.get(listId) || [];
|
|
try {
|
|
const remoteTasks = await fetchSynologyTasks(synoServerUrl, synoUsername, synoPassword, listId);
|
|
// Remap ID: the fetchSynologyTasks prefixes ids with "synology::"
|
|
const remoteMap = new Map(remoteTasks.map(t => [t.id.replace(/^synology::/, ''), t]));
|
|
const existingExternalIds = new Set(localTasks.map(t => t.externalId?.replace(/^synology::/, '')));
|
|
|
|
for (const localTask of localTasks) {
|
|
const cleanExtId = localTask.externalId?.replace(/^synology::/, '');
|
|
const remote = cleanExtId ? remoteMap.get(cleanExtId) : null;
|
|
|
|
if (!remote) {
|
|
await prisma.task.update({ where: { id: localTask.id }, data: { deletedAt: new Date() } });
|
|
deleted++;
|
|
continue;
|
|
}
|
|
|
|
const updateData: any = { lastSyncedAt: new Date() };
|
|
const remoteCompleted = remote.status === 'completed';
|
|
if (remoteCompleted !== localTask.completed) updateData.completed = remoteCompleted;
|
|
if (remote.title && remote.title !== localTask.title) updateData.title = remote.title;
|
|
|
|
await prisma.task.update({ where: { id: localTask.id }, data: updateData });
|
|
if (Object.keys(updateData).length > 1) updated++;
|
|
}
|
|
|
|
// Create new local tasks from remote
|
|
const somedayListInfo = synoListIdToSomedayList.get(listId);
|
|
if (somedayListInfo) {
|
|
const newRemoteTasks = remoteTasks.filter(rt => {
|
|
const cleanId = rt.id.replace(/^synology::/, '');
|
|
return !existingExternalIds.has(cleanId);
|
|
});
|
|
|
|
for (const remote of newRemoteTasks) {
|
|
if (!remote.title?.trim()) continue;
|
|
await prisma.task.create({
|
|
data: {
|
|
userId: user.id,
|
|
title: remote.title,
|
|
description: remote.notes || null,
|
|
completed: remote.status === 'completed',
|
|
somedayListId: somedayListInfo.id,
|
|
externalId: remote.id,
|
|
externalProvider: 'synology',
|
|
externalListId: listId,
|
|
scheduledDate: remote.due ? new Date(remote.due) : null,
|
|
lastSyncedAt: new Date(),
|
|
}
|
|
});
|
|
existingExternalIds.add(remote.id.replace(/^synology::/, ''));
|
|
created++;
|
|
}
|
|
}
|
|
} catch (listError) {
|
|
console.error(`Error syncing Synology list ${listId}:`, listError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ success: true, updated, deleted, created });
|
|
|
|
} catch (error: unknown) {
|
|
console.error('Pull sync error:', error);
|
|
const message = error instanceof Error ? error.message : 'Pull sync failed';
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function PATCH(req: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
if (!session?.user?.email) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const body = await req.json();
|
|
const { taskId, completed, title, action, scheduledDate, notes } = body;
|
|
|
|
if (!taskId) {
|
|
return NextResponse.json({ error: 'Task ID required' }, { status: 400 });
|
|
}
|
|
|
|
const task = await prisma.task.findUnique({
|
|
where: { id: taskId },
|
|
include: { user: true }
|
|
});
|
|
|
|
if (!task) {
|
|
return NextResponse.json({ error: 'Task not found' }, { status: 404 });
|
|
}
|
|
|
|
if (task.user.email !== session.user.email) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 });
|
|
}
|
|
|
|
// Only sync if external ID is present
|
|
if (task.externalId && task.externalProvider) {
|
|
|
|
if (task.externalProvider === 'google' && task.externalListId) {
|
|
const account = await prisma.account.findFirst({
|
|
where: { userId: task.userId, provider: { in: ['google-calendar', 'google'] } }
|
|
});
|
|
|
|
if (account && account.access_token) {
|
|
const client = createGoogleClient(account.access_token, account.refresh_token || undefined);
|
|
|
|
if (action === 'delete') {
|
|
await deleteGoogleTask(client, task.externalListId, task.externalId);
|
|
} else {
|
|
const updates: { title?: string; notes?: string; status?: 'needsAction' | 'completed'; due?: string | null } = {};
|
|
if (title !== undefined) updates.title = title;
|
|
if (notes !== undefined) updates.notes = notes;
|
|
if (completed !== undefined) updates.status = completed ? 'completed' : 'needsAction';
|
|
if (scheduledDate !== undefined) {
|
|
updates.due = scheduledDate ? new Date(scheduledDate).toISOString() : null;
|
|
}
|
|
if (Object.keys(updates).length > 0) {
|
|
await updateGoogleTask(client, task.externalListId, task.externalId, updates);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (task.externalProvider === 'outlook' && task.externalListId) {
|
|
const outlookToken = await getOutlookAccessToken(task.userId);
|
|
|
|
if (outlookToken) {
|
|
if (action === 'delete') {
|
|
await deleteMsTodoTask(outlookToken, task.externalListId, task.externalId);
|
|
} else {
|
|
const updates: { title?: string; body?: string; status?: 'notStarted' | 'completed'; dueDateTime?: string | null } = {};
|
|
if (title !== undefined) updates.title = title;
|
|
if (notes !== undefined) updates.body = notes;
|
|
if (completed !== undefined) updates.status = completed ? 'completed' : 'notStarted';
|
|
if (scheduledDate !== undefined) {
|
|
updates.dueDateTime = scheduledDate ? new Date(scheduledDate).toISOString() : null;
|
|
}
|
|
if (Object.keys(updates).length > 0) {
|
|
await updateMsTodoTask(outlookToken, task.externalListId, task.externalId, updates);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update lastSyncedAt
|
|
await prisma.task.update({
|
|
where: { id: taskId },
|
|
data: { lastSyncedAt: new Date() }
|
|
});
|
|
}
|
|
|
|
// Update local task state
|
|
const updateData: any = {};
|
|
if (completed !== undefined) updateData.completed = completed;
|
|
if (title !== undefined) updateData.title = title;
|
|
if (scheduledDate !== undefined) updateData.scheduledDate = scheduledDate ? new Date(scheduledDate) : null;
|
|
|
|
if (Object.keys(updateData).length > 0) {
|
|
const updatedTask = await prisma.task.update({
|
|
where: { id: taskId },
|
|
data: updateData
|
|
});
|
|
return NextResponse.json({ success: true, task: updatedTask });
|
|
}
|
|
|
|
return NextResponse.json({ success: true });
|
|
|
|
} catch (error: unknown) {
|
|
console.error('Sync error:', error);
|
|
const message = error instanceof Error ? error.message : 'Sync failed';
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// POST - Push a newly created local task to the external provider
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
if (!session?.user?.email) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const body = await req.json();
|
|
const { taskId } = body;
|
|
|
|
if (!taskId) {
|
|
return NextResponse.json({ error: 'Task ID required' }, { status: 400 });
|
|
}
|
|
|
|
const task = await prisma.task.findUnique({
|
|
where: { id: taskId },
|
|
include: { user: true, somedayList: true }
|
|
});
|
|
|
|
if (!task) {
|
|
return NextResponse.json({ error: 'Task not found' }, { status: 404 });
|
|
}
|
|
|
|
if (task.user.email !== session.user.email) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 });
|
|
}
|
|
|
|
// Task must be in a someday list that has an external link
|
|
if (!task.somedayList?.externalId || !task.somedayList?.externalProvider) {
|
|
return NextResponse.json({ error: 'List is not linked to an external provider' }, { status: 400 });
|
|
}
|
|
|
|
const provider = task.somedayList.externalProvider;
|
|
const listExternalId = task.somedayList.externalId;
|
|
|
|
if (provider === 'outlook') {
|
|
const outlookToken = await getOutlookAccessToken(task.userId);
|
|
if (!outlookToken) {
|
|
return NextResponse.json({ error: 'Outlook token not available' }, { status: 400 });
|
|
}
|
|
|
|
const created = await createMsTodoTask(outlookToken, listExternalId, {
|
|
title: task.title,
|
|
body: task.description || undefined,
|
|
dueDateTime: task.scheduledDate ? task.scheduledDate.toISOString() : undefined,
|
|
});
|
|
|
|
const updatedTask = await prisma.task.update({
|
|
where: { id: taskId },
|
|
data: {
|
|
externalId: created.id,
|
|
externalProvider: 'outlook',
|
|
externalListId: listExternalId,
|
|
lastSyncedAt: new Date(),
|
|
}
|
|
});
|
|
|
|
return NextResponse.json({ success: true, task: updatedTask });
|
|
}
|
|
|
|
if (provider === 'google') {
|
|
const account = await prisma.account.findFirst({
|
|
where: { userId: task.userId, provider: { in: ['google-calendar', 'google'] } }
|
|
});
|
|
if (!account?.access_token) {
|
|
return NextResponse.json({ error: 'Google token not available' }, { status: 400 });
|
|
}
|
|
|
|
const client = createGoogleClient(account.access_token, account.refresh_token || undefined);
|
|
|
|
const created = await createGoogleTask(client, listExternalId, {
|
|
title: task.title,
|
|
notes: task.description || undefined,
|
|
due: task.scheduledDate ? task.scheduledDate.toISOString() : undefined,
|
|
});
|
|
|
|
const updatedTask = await prisma.task.update({
|
|
where: { id: taskId },
|
|
data: {
|
|
externalId: created.id,
|
|
externalProvider: 'google',
|
|
externalListId: listExternalId,
|
|
lastSyncedAt: new Date(),
|
|
}
|
|
});
|
|
|
|
return NextResponse.json({ success: true, task: updatedTask });
|
|
}
|
|
|
|
return NextResponse.json({ error: `Provider "${provider}" creation sync not supported yet` }, { status: 400 });
|
|
|
|
} catch (error: unknown) {
|
|
console.error('Task creation sync error:', error);
|
|
const message = error instanceof Error ? error.message : 'Task creation sync failed';
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|