Skip to content

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 22 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6e73298
feat: queue task for trial
DennieDan Jun 14, 2025
cb01720
style: apply prettier formatting
DennieDan Jun 14, 2025
c8a8b01
style: apply prettier formatting for feat/setup-trigger (#3093)
DennieDan Jun 15, 2025
64b3b5e
fix: remove unused code
DennieDan Jun 15, 2025
a94c3b1
Merge remote-tracking branch 'origin/feat/setup-trigger'
DennieDan Jun 15, 2025
5ebbe46
fix: connect local dev to server for running queued tasks
DennieDan Jun 15, 2025
639f129
feat: create scheduled background task
DennieDan Jun 15, 2025
f9f3755
feat: define google fetching function
DennieDan Jun 16, 2025
42bca59
feat: add background sync from google to tuturuuu
DennieDan Jun 16, 2025
4584f75
feat: setup trigger for Google Calendar sync
DennieDan Jun 16, 2025
4d4de39
chore: add .env.example in root directory
DennieDan Jun 16, 2025
2f3b2af
chore: move .env.local to packages/trigger/
DennieDan Jun 17, 2025
49978b8
feat: add changes using cursor
DennieDan Jun 17, 2025
2d4a1e9
fix: export example.ts task
DennieDan Jun 17, 2025
a002eaa
feat: add current view within background sync range check
DennieDan Jun 17, 2025
70e3e99
fix: export example.ts instead of google.ts
DennieDan Jun 17, 2025
ef6cbfa
fix: fix to 4 weeks start from current week not from now
DennieDan Jun 17, 2025
555e347
chore: merge with feat/setup-trigger
DennieDan Jun 17, 2025
8747e54
chore(trigger): move .env.example to the trigger package
vhpx Jun 17, 2025
a99909f
Merge branch 'main' into feat/setup-trigger
vhpx Jun 17, 2025
d419d5c
chore(db): consolidate database schema
vhpx Jun 17, 2025
ae47ade
chore(db): update database schema with new types and argument order a…
vhpx Jun 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .env.example
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
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ dist
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env
.env.local
.env.development.local
.env.test.local
Expand All @@ -38,4 +38,10 @@ yarn-error.log*

# Typescript
*.tsbuildinfo
next-env.d.ts
next-env.d.ts
.trigger.env
.env.local
.env.*.local

# Trigger.dev
.trigger/
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
}
};

const triggerHelloWorld = async () => {
const res = await fetch('/api/hello-world');
const data = await res.json();
console.log(data);
};

Check warning on line 62 in apps/web/src/app/[locale]/(dashboard)/[wsId]/calendar/active-sync.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/[locale]/(dashboard)/[wsId]/calendar/active-sync.tsx#L57-L62

Added lines #L57 - L62 were not covered by tests
return (
<div>
<div className="mb-4 flex gap-2">
Expand Down Expand Up @@ -85,6 +91,7 @@
>
Switch to month
</Button>
<Button onClick={() => triggerHelloWorld()}>Trigger Hello World</Button>

Check warning on line 94 in apps/web/src/app/[locale]/(dashboard)/[wsId]/calendar/active-sync.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/[locale]/(dashboard)/[wsId]/calendar/active-sync.tsx#L94

Added line #L94 was not covered by tests
</div>

{/* Add sync progress bar when syncing */}
Expand Down
16 changes: 16 additions & 0 deletions apps/web/src/app/api/hello-world/route.ts
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';

Check warning on line 2 in apps/web/src/app/api/hello-world/route.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/api/hello-world/route.ts#L2

Added line #L2 was not covered by tests
import type { helloWorldTask } from '@tuturuuu/trigger/example';
import { NextResponse } from 'next/server';

Check warning on line 4 in apps/web/src/app/api/hello-world/route.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/api/hello-world/route.ts#L4

Added line #L4 was not covered by tests

//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'
);

Check warning on line 13 in apps/web/src/app/api/hello-world/route.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/api/hello-world/route.ts#L9-L13

Added lines #L9 - L13 were not covered by tests

return NextResponse.json(handle);
}

Check warning on line 16 in apps/web/src/app/api/hello-world/route.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/api/hello-world/route.ts#L15-L16

Added lines #L15 - L16 were not covered by tests
280 changes: 243 additions & 37 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,19 @@
"update-all": "(cd apps && for dir in */; do echo \"Updating apps/$dir...\" && cd \"$dir\" && bun update && cd ..; done) && (cd packages && for dir in */; do echo \"Updating packages/$dir...\" && cd \"$dir\" && bun update && cd ..; done)"
},
"dependencies": {
"@trigger.dev/sdk": "^3.3.17",
"cookies-next": "^6.0.0",
"react-scan": "^0.3.4"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.28.0",
"@trigger.dev/build": "^3.3.17",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@tuturuuu/eslint-config": "workspace:*",
"@tuturuuu/typescript-config": "workspace:*",
"@vitest/coverage-v8": "^3.2.3",
"concurrently": "^9.1.2",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"prettier-eslint": "^16.4.2",
Expand Down
16 changes: 16 additions & 0 deletions packages/trigger/example.ts
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',

Check warning on line 4 in packages/trigger/example.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/example.ts#L3-L4

Added lines #L3 - L4 were not covered by tests
// 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 });

Check warning on line 8 in packages/trigger/example.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/example.ts#L6-L8

Added lines #L6 - L8 were not covered by tests

await wait.for({ seconds: 5 });

Check warning on line 10 in packages/trigger/example.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/example.ts#L10

Added line #L10 was not covered by tests

return {
message: 'Hello, world!',
};
},
});

Check warning on line 16 in packages/trigger/example.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/example.ts#L12-L16

Added lines #L12 - L16 were not covered by tests
35 changes: 35 additions & 0 deletions packages/trigger/first-scheduled-task.ts
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: {

Check warning on line 5 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L3-L5

Added lines #L3 - L5 were not covered by tests
// every 1 minute
pattern: '*/1 * * * *',
},
run: async (payload) => {

Check warning on line 9 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L7-L9

Added lines #L7 - L9 were not covered by tests
//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

Check warning on line 12 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L12

Added line #L12 was not covered by tests

//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

Check warning on line 16 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L16

Added line #L16 was not covered by tests

//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

Check warning on line 21 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L21

Added line #L21 was not covered by tests

//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

Check warning on line 25 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L25

Added line #L25 was not covered by tests

//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

Check warning on line 30 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L30

Added line #L30 was not covered by tests

//the next 5 dates this task is scheduled to run
console.log(payload.upcoming); //is an array of Date objects
},
});

Check warning on line 35 in packages/trigger/first-scheduled-task.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/first-scheduled-task.ts#L33-L35

Added lines #L33 - L35 were not covered by tests
150 changes: 150 additions & 0 deletions packages/trigger/google-calendar-background-sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { createClient } from '@supabase/supabase-js';
import { schedules } from '@trigger.dev/sdk/v3';
import { OAuth2Client } from 'google-auth-library';
import { google } from 'googleapis';

Check warning on line 4 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L2-L4

Added lines #L2 - L4 were not covered by tests

export const googleCalendarBackgroundSync = schedules.task({
id: 'google-calendar-background-sync',
cron: {

Check warning on line 8 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L6-L8

Added lines #L6 - L8 were not covered by tests
// every 2 minutes
pattern: '*/2 * * * *',
},
run: async () => {
console.log(
'process.env.NEXT_PUBLIC_SUPABASE_URL',
process.env.NEXT_PUBLIC_SUPABASE_URL
);

Check warning on line 16 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L10-L16

Added lines #L10 - L16 were not covered by tests
// Initialize Supabase client inside the task function
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
);

Check warning on line 21 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L18-L21

Added lines #L18 - L21 were not covered by tests

await syncGoogleCalendarEvents(supabase);
console.log('Synced events from all linked Google accounts');
},
});

Check warning on line 26 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L23-L26

Added lines #L23 - L26 were not covered by tests

const getGoogleAuthClient = (tokens: {

Check warning on line 28 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L28

Added line #L28 was not covered by tests
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,
});

Check warning on line 36 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L31-L36

Added lines #L31 - L36 were not covered by tests

oauth2Client.setCredentials(tokens);
return oauth2Client;
};

Check warning on line 40 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L38-L40

Added lines #L38 - L40 were not covered by tests

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';
};

Check warning on line 56 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L42-L56

Added lines #L42 - L56 were not covered by tests

const syncGoogleCalendarEvents = async (supabase: any) => {
try {

Check warning on line 59 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L58-L59

Added lines #L58 - L59 were not covered by tests
// Fetch all wsId with auth tokens not null
const result = await supabase
.from('calendar_auth_tokens')
.select('ws_id, access_token, refresh_token');

Check warning on line 63 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L61-L63

Added lines #L61 - L63 were not covered by tests

const data = result.data;
const error = result.error;

Check warning on line 66 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L65-L66

Added lines #L65 - L66 were not covered by tests

if (error) {
console.error('Error fetching auth tokens:', error);
return [];
}

Check warning on line 71 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L68-L71

Added lines #L68 - L71 were not covered by tests

const googleTokens = data.map((item: any) => ({
ws_id: item.ws_id,
access_token: item.access_token,
refresh_token: item.refresh_token,
}));

Check warning on line 77 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L73-L77

Added lines #L73 - L77 were not covered by tests
// Type assertion for the tokens
const tokens = googleTokens as

Check warning on line 79 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L79

Added line #L79 was not covered by tests
| {
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,
});
}

Check warning on line 95 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L87-L95

Added lines #L87 - L95 were not covered by tests

try {
const auth = getGoogleAuthClient(token);
const calendar = google.calendar({ version: 'v3', auth });

Check warning on line 99 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L97-L99

Added lines #L97 - L99 were not covered by tests

const timeMin = new Date();
const timeMax = new Date();
timeMax.setDate(timeMax.getDate() + 28);

Check warning on line 103 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L101-L103

Added lines #L101 - L103 were not covered by tests

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,
});

Check warning on line 112 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L105-L112

Added lines #L105 - L112 were not covered by tests

const events = response.data.items || [];

Check warning on line 114 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L114

Added line #L114 was not covered by tests

// 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);

Check warning on line 131 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L117-L131

Added lines #L117 - L131 were not covered by tests

// upsert the events in the database for this wsId
const { error } = await supabase
.from('workspace_calendar_events')
.upsert(formattedEvents, {
onConflict: 'google_event_id',
});
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 [];
}
};

Check warning on line 150 in packages/trigger/google-calendar-background-sync.ts

View check run for this annotation

Codecov / codecov/patch

packages/trigger/google-calendar-background-sync.ts#L134-L150

Added lines #L134 - L150 were not covered by tests
21 changes: 21 additions & 0 deletions packages/trigger/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@tuturuuu/trigger",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "./example.ts",
"types": "./example.ts",
"exports": {
".": {
"types": "./example.ts",
"import": "./example.ts"
},
"./example": {
"types": "./example.ts",
"import": "./example.ts"
}
},
"dependencies": {
"@trigger.dev/sdk": "^3.0.0"
}
}
Loading