Skip to content

Commit 5b4b08e

Browse files
committed
fix(calendar-rate): adjust google calendar api rate limit
1 parent b4df249 commit 5b4b08e

File tree

3 files changed

+66
-26
lines changed

3 files changed

+66
-26
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"next": "^16.0.0",
3434
"next-themes": "^0.4.6",
3535
"nodemailer": "^7.0.5",
36+
"p-limit": "^7.2.0",
3637
"pg": "^8.15.6",
3738
"react": "^19.0.0",
3839
"react-day-picker": "^9.11.1",

src/app/api/calendar/notion-webhook/route.ts

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { NextRequest, NextResponse } from "next/server";
2+
import pLimit from "p-limit";
23
import { Client } from "@notionhq/client";
4+
import { google } from "googleapis";
35
import crypto from "crypto";
46
import fs from "fs/promises";
57
import path from "path";
@@ -10,7 +12,7 @@ import {
1012
NotionApiResponse,
1113
mapNotionResultToPage,
1214
} from "@/types/notion";
13-
import { syncAllEventsToCalendar, getOrCreateUserCalendar } from "@/utils/googleCalendar";
15+
import { syncAllEventsToCalendar } from "@/utils/googleCalendar";
1416
import { getAllUsers } from "@/utils/dbUtils";
1517

1618
const NOTION_API_KEY = process.env.NOTION_API_KEY!;
@@ -58,35 +60,60 @@ async function fetchAllNotionEvents(): Promise<NotionEvent[]> {
5860
return pages.map(parseNotionPageToEvent);
5961
}
6062

63+
async function getExistingNEIISTCalendars() {
64+
const SCOPES = ["https://www.googleapis.com/auth/calendar"];
65+
const serviceAccountKey = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_KEY!);
66+
const auth = new google.auth.GoogleAuth({
67+
credentials: serviceAccountKey,
68+
scopes: SCOPES,
69+
});
70+
const calendar = google.calendar({ version: "v3", auth });
71+
72+
const response = await calendar.calendarList.list();
73+
const calendars = response.data.items || [];
74+
return calendars.filter((cal) => cal.summary?.startsWith("NEIIST-"));
75+
}
76+
6177
async function syncAllEventsToGoogleCalendars(events: NotionEvent[]) {
6278
const allUsers = await getAllUsers();
63-
const users = allUsers.filter((u) => u.email && u.istid);
79+
const existingCalendars = await getExistingNEIISTCalendars();
80+
const calendarByIstid = new Map<string, { id: string; summary: string }>();
81+
82+
existingCalendars.forEach((cal) => {
83+
const istidMatch = cal.description?.match(/istid:([a-zA-Z0-9]+)/);
84+
if (istidMatch) {
85+
calendarByIstid.set(istidMatch[1], { id: cal.id!, summary: cal.summary! });
86+
}
87+
});
88+
89+
const usersWithCalendars = allUsers.filter(
90+
(u) => u.email && u.istid && calendarByIstid.has(u.istid)
91+
);
92+
93+
const limit = pLimit(2);
6494

6595
const results = await Promise.all(
66-
users.map(async (user) => {
67-
try {
68-
const alternativeEmailRaw = user.alternativeEmailVerified
69-
? user.alternativeEmail
70-
: undefined;
71-
const alternativeEmail = alternativeEmailRaw ?? undefined;
72-
const calendarId = await getOrCreateUserCalendar(
73-
user.email!,
74-
user.istid,
75-
user.name,
76-
alternativeEmail
77-
);
78-
const stats = await syncAllEventsToCalendar(
79-
calendarId,
80-
events,
81-
user.email!,
82-
alternativeEmail
83-
);
84-
return stats;
85-
} catch (error) {
86-
console.error(`✗ Error syncing calendar for ${user.istid}:`, error);
87-
return { updated: 0, deleted: 0, unchanged: 0 };
88-
}
89-
})
96+
usersWithCalendars.map((user) =>
97+
limit(async () => {
98+
try {
99+
const alternativeEmailRaw = user.alternativeEmailVerified
100+
? user.alternativeEmail
101+
: undefined;
102+
const alternativeEmail = alternativeEmailRaw ?? undefined;
103+
const calendarId = calendarByIstid.get(user.istid)!.id;
104+
const stats = await syncAllEventsToCalendar(
105+
calendarId,
106+
events,
107+
user.email!,
108+
alternativeEmail
109+
);
110+
return stats;
111+
} catch (error) {
112+
console.error(`Error syncing calendar for ${user.istid}:`, error);
113+
return { updated: 0, deleted: 0, unchanged: 0 };
114+
}
115+
})
116+
)
90117
);
91118

92119
const totals = results.reduce(

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2991,6 +2991,13 @@ p-limit@^3.0.2:
29912991
dependencies:
29922992
yocto-queue "^0.1.0"
29932993

2994+
p-limit@^7.2.0:
2995+
version "7.2.0"
2996+
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-7.2.0.tgz#afcf6b5a86d093660140497dda0e640dd01a7b3b"
2997+
integrity sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ==
2998+
dependencies:
2999+
yocto-queue "^1.2.1"
3000+
29943001
p-locate@^4.1.0:
29953002
version "4.1.0"
29963003
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
@@ -4013,3 +4020,8 @@ yocto-queue@^0.1.0:
40134020
version "0.1.0"
40144021
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
40154022
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
4023+
4024+
yocto-queue@^1.2.1:
4025+
version "1.2.1"
4026+
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418"
4027+
integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==

0 commit comments

Comments
 (0)