My-Weekly-ToDo-List/src/lib/google-tasks.ts
mARTin dd8d471a1d fix: Switch Apple Reminders to CalDAV, restore date picker, add goal scope
- Replace broken CloudKit/pyicloud Apple Reminders integration with
  CalDAV-based approach (getAppleReminderLists, fetchTasks from
  apple-calendar.ts) in tasks/lists, tasks/import, and tasks/sync routes
- Restore calendar date picker icon in header for jump-to-date navigation
- Add goalScope field to User model with migration
- Force-dynamic Google OAuth routes to fix redirect issues
- Update Google Tasks client and sync logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:23:37 +01:00

152 lines
5.0 KiB
TypeScript

import { google } from 'googleapis';
import { OAuth2Client } from 'google-auth-library';
export interface GoogleTaskList {
id: string;
title: string;
updated: string;
}
export interface GoogleTask {
id: string;
title: string;
notes?: string;
status: string;
due?: string;
updated: string;
}
/**
* Create an authenticated Google OAuth2 client
*/
export const createGoogleClient = (accessToken: string, refreshToken?: string): OAuth2Client => {
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);
oauth2Client.setCredentials({
access_token: accessToken,
refresh_token: refreshToken
});
return oauth2Client;
};
/**
* Fetch all task lists for the user
*/
export const fetchGoogleTaskLists = async (client: OAuth2Client): Promise<GoogleTaskList[]> => {
const service = google.tasks({ version: 'v1', auth: client });
try {
const response = await service.tasklists.list();
return (response.data.items || []).map(item => ({
id: item.id!,
title: item.title!,
updated: item.updated!
}));
} catch (error) {
console.error('Error fetching Google Task lists:', error);
throw error;
}
};
/**
* Fetch tasks from a specific task list
*/
export const fetchGoogleTasks = async (client: OAuth2Client, taskListId: string): Promise<GoogleTask[]> => {
const service = google.tasks({ version: 'v1', auth: client });
try {
const response = await service.tasks.list({
tasklist: taskListId,
showCompleted: false, // We usually only want active tasks for import
showHidden: false
});
return (response.data.items || []).map(item => ({
id: item.id!,
title: item.title!,
notes: item.notes || undefined,
status: item.status!,
due: item.due || undefined,
updated: item.updated!
}));
} catch (error) {
console.error(`Error fetching Google Tasks from list ${taskListId}:`, error);
throw error;
}
};
/**
* Update a Google Task status
*/
export const updateGoogleTask = async (client: OAuth2Client, taskListId: string, taskId: string, updates: { title?: string; notes?: string; status?: 'needsAction' | 'completed'; due?: string | null }): Promise<GoogleTask> => {
const service = google.tasks({ version: 'v1', auth: client });
try {
const requestBody: any = {};
if (updates.title !== undefined) requestBody.title = updates.title;
if (updates.notes !== undefined) requestBody.notes = updates.notes;
if (updates.due !== undefined) requestBody.due = updates.due;
if (updates.status !== undefined) {
requestBody.status = updates.status;
requestBody.completed = updates.status === 'completed' ? new Date().toISOString() : null;
}
const response = await service.tasks.patch({
tasklist: taskListId,
task: taskId,
requestBody,
});
const item = response.data;
return {
id: item.id!,
title: item.title!,
notes: item.notes || undefined,
status: item.status!,
due: item.due || undefined,
updated: item.updated!
};
} catch (error) {
console.error(`Error updating Google Task ${taskId}:`, error);
throw error;
}
};
export const deleteGoogleTask = async (client: OAuth2Client, taskListId: string, taskId: string): Promise<void> => {
const service = google.tasks({ version: 'v1', auth: client });
try {
await service.tasks.delete({
tasklist: taskListId,
task: taskId,
});
} catch (error) {
console.error(`Error deleting Google Task ${taskId}:`, error);
throw error;
}
};
export const updateGoogleTaskStatus = async (client: OAuth2Client, taskListId: string, taskId: string, status: 'needsAction' | 'completed'): Promise<GoogleTask> => {
const service = google.tasks({ version: 'v1', auth: client });
try {
const response = await service.tasks.patch({
tasklist: taskListId,
task: taskId,
requestBody: {
status: status,
completed: status === 'completed' ? new Date().toISOString() : null
}
});
const item = response.data;
return {
id: item.id!,
title: item.title!,
notes: item.notes || undefined,
status: item.status!,
due: item.due || undefined,
updated: item.updated!
};
} catch (error) {
console.error(`Error updating Google Task ${taskId} in list ${taskListId}:`, error);
throw error;
}
};