-
-
Notifications
You must be signed in to change notification settings - Fork 19
Set up Background sync using Trigger.dev #3092
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
6e73298
feat: queue task for trial
DennieDan cb01720
style: apply prettier formatting
DennieDan c8a8b01
style: apply prettier formatting for feat/setup-trigger (#3093)
DennieDan 64b3b5e
fix: remove unused code
DennieDan a94c3b1
Merge remote-tracking branch 'origin/feat/setup-trigger'
DennieDan 5ebbe46
fix: connect local dev to server for running queued tasks
DennieDan 639f129
feat: create scheduled background task
DennieDan f9f3755
feat: define google fetching function
DennieDan 42bca59
feat: add background sync from google to tuturuuu
DennieDan 4584f75
feat: setup trigger for Google Calendar sync
DennieDan 4d4de39
chore: add .env.example in root directory
DennieDan 2f3b2af
chore: move .env.local to packages/trigger/
DennieDan 49978b8
feat: add changes using cursor
DennieDan 2d4a1e9
fix: export example.ts task
DennieDan a002eaa
feat: add current view within background sync range check
DennieDan 70e3e99
fix: export example.ts instead of google.ts
DennieDan ef6cbfa
fix: fix to 4 weeks start from current week not from now
DennieDan 555e347
chore: merge with feat/setup-trigger
DennieDan 8747e54
chore(trigger): move .env.example to the trigger package
vhpx a99909f
Merge branch 'main' into feat/setup-trigger
vhpx d419d5c
chore(db): consolidate database schema
vhpx ae47ade
chore(db): update database schema with new types and argument order a…
vhpx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Required environment variables | ||
# for both development and production | ||
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL | ||
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY | ||
SUPABASE_SERVICE_KEY=YOUR_SUPABASE_SERVICE_KEY | ||
|
||
# Optional API keys | ||
OPENAI_API_KEY=YOUR_OPENAI_API_KEY | ||
ANTHROPIC_API_KEY=YOUR_ANTHROPIC_API_KEY | ||
GOOGLE_GENERATIVE_AI_API_KEY=YOUR_GOOGLE_GENERATIVE_AI_API_KEY | ||
|
||
# Google Vertex AI credentials | ||
GOOGLE_VERTEX_PROJECT=YOUR_GOOGLE_VERTEX_PROJECT | ||
GOOGLE_VERTEX_LOCATION=YOUR_GOOGLE_VERTEX_LOCATION | ||
GOOGLE_APPLICATION_CREDENTIALS=YOUR_GOOGLE_APPLICATION_CREDENTIALS | ||
|
||
# Google Calendar API credentials | ||
GOOGLE_CLIENT_ID=YOUR_GOOGLE_CLIENT_ID | ||
GOOGLE_CLIENT_SECRET=YOUR_GOOGLE_CLIENT_SECRET | ||
GOOGLE_REDIRECT_URI=YOUR_GOOGLE_REDIRECT_URI | ||
|
||
# AWS Credentials | ||
AWS_REGION=YOUR_AWS_REGION | ||
AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID | ||
AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET | ||
SOURCE_NAME=YOUR_SOURCE_NAME | ||
SOURCE_EMAIL=YOUR_SOURCE_EMAIL | ||
|
||
DEEPGRAM_ENV=YOUR_DEEPGRAM_ENV | ||
DEEPGRAM_API_KEY=YOUR_DEEPGRAM_API_KEY | ||
|
||
MODAL_TOKEN_ID=YOUR_MODAL_TOKEN_ID | ||
MODAL_TOKEN_SECRET=YOUR_MODAL_TOKEN_SECRET | ||
|
||
CF_ACCOUNT_ID=YOUR_CF_ACCOUNT_ID | ||
CF_API_TOKEN=YOUR_CF_API_TOKEN | ||
|
||
# Infrastructure Credentials | ||
SCRAPER_URL=YOUR_SCRAPER_URL | ||
AURORA_EXTERNAL_URL=YOUR_AURORA_EXTERNAL_URL | ||
AURORA_EXTERNAL_WSID=YOUR_AURORA_EXTERNAL_WSID | ||
PROXY_API_KEY=YOUR_PROXY_API_KEY | ||
NEXT_PUBLIC_PROXY_API_KEY=YOUR_NEXT_PUBLIC_PROXY_API_KEY |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction | ||
import { tasks } from '@trigger.dev/sdk/v3'; | ||
import type { helloWorldTask } from '@tuturuuu/trigger/example'; | ||
import { NextResponse } from 'next/server'; | ||
|
||
//tasks.trigger also works with the edge runtime | ||
//export const runtime = "edge"; | ||
|
||
export async function GET() { | ||
const handle = await tasks.trigger<typeof helloWorldTask>( | ||
'hello-world', | ||
'James' | ||
); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return NextResponse.json(handle); | ||
} | ||
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { logger, task, wait } from '@trigger.dev/sdk/v3'; | ||
|
||
export const helloWorldTask = task({ | ||
id: 'hello-world', | ||
// Set an optional maxDuration to prevent tasks from running indefinitely | ||
maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute | ||
run: async (payload: any, { ctx }) => { | ||
logger.log('Hello, world!', { payload, ctx }); | ||
|
||
await wait.for({ seconds: 5 }); | ||
|
||
return { | ||
message: 'Hello, world!', | ||
}; | ||
}, | ||
}); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { schedules } from '@trigger.dev/sdk/v3'; | ||
|
||
export const firstScheduledTask = schedules.task({ | ||
id: 'first-scheduled-task', | ||
cron: { | ||
// every 1 minute | ||
pattern: '*/1 * * * *', | ||
}, | ||
run: async (payload) => { | ||
//when the task was scheduled to run | ||
//note this will be slightly different from new Date() because it takes a few ms to run the task | ||
console.log(payload.timestamp); //is a Date object | ||
|
||
//when the task was last run | ||
//this can be undefined if it's never been run | ||
console.log(payload.lastTimestamp); //is a Date object or undefined | ||
|
||
//the timezone the schedule was registered with, defaults to "UTC" | ||
//this is in IANA format, e.g. "America/New_York" | ||
//See the full list here: https://cloud.trigger.dev/timezones | ||
console.log(payload.timezone); //is a string | ||
|
||
//the schedule id (you can have many schedules for the same task) | ||
//using this you can remove the schedule, update it, etc | ||
console.log(payload.scheduleId); //is a string | ||
|
||
//you can optionally provide an external id when creating the schedule | ||
//usually you would set this to a userId or some other unique identifier | ||
//this can be undefined if you didn't provide one | ||
console.log(payload.externalId); //is a string or undefined | ||
|
||
//the next 5 dates this task is scheduled to run | ||
console.log(payload.upcoming); //is an array of Date objects | ||
}, | ||
}); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { createClient } from '@supabase/supabase-js'; | ||
import { schedules } from '@trigger.dev/sdk/v3'; | ||
import 'dotenv/config'; | ||
import { OAuth2Client } from 'google-auth-library'; | ||
import { google } from 'googleapis'; | ||
|
||
export const googleCalendarBackgroundSync = schedules.task({ | ||
id: 'google-calendar-background-sync', | ||
cron: { | ||
// every 2 minutes | ||
pattern: '*/2 * * * *', | ||
}, | ||
run: async () => { | ||
console.log( | ||
'process.env.NEXT_PUBLIC_SUPABASE_URL', | ||
process.env.NEXT_PUBLIC_SUPABASE_URL | ||
); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Initialize Supabase client inside the task function | ||
const supabase = createClient( | ||
process.env.NEXT_PUBLIC_SUPABASE_URL!, | ||
process.env.SUPABASE_SERVICE_KEY! | ||
); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
await syncGoogleCalendarEvents(supabase); | ||
console.log('Synced events from all linked Google accounts'); | ||
}, | ||
}); | ||
|
||
const getGoogleAuthClient = (tokens: { | ||
access_token: string; | ||
refresh_token?: string; | ||
}) => { | ||
const oauth2Client = new OAuth2Client({ | ||
clientId: process.env.GOOGLE_CLIENT_ID, | ||
clientSecret: process.env.GOOGLE_CLIENT_SECRET, | ||
redirectUri: process.env.GOOGLE_REDIRECT_URI, | ||
}); | ||
|
||
oauth2Client.setCredentials(tokens); | ||
return oauth2Client; | ||
}; | ||
|
||
const getColorFromGoogleColorId = (colorId?: string): string => { | ||
const colorMap: Record<string, string> = { | ||
'1': 'RED', | ||
'2': 'GREEN', | ||
'3': 'GRAY', | ||
'4': 'PINK', | ||
'5': 'YELLOW', | ||
'6': 'ORANGE', | ||
'8': 'CYAN', | ||
'9': 'PURPLE', | ||
'10': 'INDIGO', | ||
'11': 'BLUE', | ||
}; | ||
return colorId && colorMap[colorId] ? colorMap[colorId] : 'BLUE'; | ||
}; | ||
|
||
const syncGoogleCalendarEvents = async (supabase: any) => { | ||
try { | ||
// Fetch all wsId with auth tokens not null | ||
const result = await supabase | ||
.from('calendar_auth_tokens') | ||
.select('ws_id, access_token, refresh_token'); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const data = result.data; | ||
const error = result.error; | ||
|
||
if (error) { | ||
console.error('Error fetching auth tokens:', error); | ||
return []; | ||
} | ||
|
||
const googleTokens = data.map((item: any) => ({ | ||
ws_id: item.ws_id, | ||
access_token: item.access_token, | ||
refresh_token: item.refresh_token, | ||
})); | ||
// Type assertion for the tokens | ||
const tokens = googleTokens as | ||
| { | ||
ws_id: string; | ||
access_token: string; | ||
refresh_token: string; | ||
}[] | ||
| null; | ||
|
||
for (const token of tokens || []) { | ||
const { ws_id, access_token, refresh_token } = token; | ||
if (!access_token) { | ||
console.error('No Google access token found for wsIds:', { | ||
ws_id, | ||
hasAccessToken: !!access_token, | ||
hasRefreshToken: !!refresh_token, | ||
}); | ||
} | ||
|
||
try { | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const auth = getGoogleAuthClient(token); | ||
const calendar = google.calendar({ version: 'v3', auth }); | ||
|
||
const timeMin = new Date(); | ||
const timeMax = new Date(); | ||
timeMax.setDate(timeMax.getDate() + 28); | ||
|
||
const response = await calendar.events.list({ | ||
calendarId: 'primary', | ||
timeMin: timeMin.toISOString(), // from now | ||
timeMax: timeMax.toISOString(), // to the next 4 weeks | ||
singleEvents: true, // separate recurring events | ||
orderBy: 'startTime', | ||
maxResults: 1000, | ||
}); | ||
|
||
const events = response.data.items || []; | ||
|
||
// format the events to match the expected structure | ||
const formattedEvents = events.map((event) => ({ | ||
google_event_id: event.id, | ||
title: event.summary || 'Untitled Event', | ||
description: event.description || '', | ||
start_at: event.start?.dateTime || event.start?.date || '', | ||
end_at: event.end?.dateTime || event.end?.date || '', | ||
location: event.location || '', | ||
color: getColorFromGoogleColorId(event.colorId ?? undefined), | ||
ws_id: ws_id, | ||
locked: false, | ||
})); | ||
console.log('ws_id', ws_id); | ||
console.log('access_token', access_token); | ||
console.log('refresh_token', refresh_token); | ||
console.log('formattedEvents', formattedEvents); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// upsert the events in the database for this wsId | ||
const { error } = await supabase | ||
.from('workspace_calendar_events') | ||
.upsert(formattedEvents, { | ||
onConflict: 'google_event_id', | ||
}); | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (error) { | ||
console.error('Error upserting events:', error); | ||
} | ||
} catch (error) { | ||
console.error('Error fetching Google Calendar events:', error); | ||
} | ||
} | ||
} catch (error) { | ||
console.error('Error in fetchGoogleCalendarEvents:', error); | ||
return []; | ||
} | ||
}; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "@tuturuuu/trigger", | ||
"version": "0.1.0", | ||
"private": true, | ||
"type": "module", | ||
vhpx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"main": "./google-calendar-background-sync.ts", | ||
"types": "./google-calendar-background-sync.ts", | ||
"exports": { | ||
".": { | ||
"types": "./google-calendar-background-sync.ts", | ||
"import": "./google-calendar-background-sync.ts" | ||
}, | ||
"./google-calendar-background-sync": { | ||
"types": "./google-calendar-background-sync.ts", | ||
"import": "./google-calendar-background-sync.ts" | ||
} | ||
}, | ||
"dependencies": { | ||
"@trigger.dev/sdk": "^3.3.17" | ||
}, | ||
"devDependencies": { | ||
"@trigger.dev/build": "^3.3.17" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { defineConfig } from '@trigger.dev/sdk/v3'; | ||
|
||
export default defineConfig({ | ||
project: 'proj_uoblreqcnhwrarddxrds', | ||
runtime: 'node', | ||
logLevel: 'log', | ||
// The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped. | ||
// You can override this on an individual task. | ||
// See https://trigger.dev/docs/runs/max-duration | ||
maxDuration: 3600, | ||
retries: { | ||
enabledInDev: true, | ||
default: { | ||
maxAttempts: 3, | ||
minTimeoutInMs: 1000, | ||
maxTimeoutInMs: 10000, | ||
factor: 2, | ||
randomize: true, | ||
}, | ||
}, | ||
dirs: ['.'], | ||
}); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.