Skip to content

Commit da14ff0

Browse files
authored
Merge pull request #276 from ReflectionsProjections/aryan/add_staff_topic
Aryan/add staff topic
2 parents aaa1e12 + d6feadc commit da14ff0

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"scripts": {
66
"dev": "nodemon -w src/ -x tsx src/app.ts",
77
"start": "tsx src/app.ts",
8+
"subscribe:staff": "tsx scr/subscribe-staff-to-topic.ts",
89
"lint": "eslint . --fix",
910
"lint:check": "eslint .",
1011
"format": "prettier . --write",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import "dotenv/config";
2+
import { SupabaseDB } from "../database";
3+
import { getFirebaseAdmin } from "../firebase";
4+
5+
async function main() {
6+
const topicName = "allStaff";
7+
// Ensure topic exists in Supabase customTopics
8+
const { data: existing } = await SupabaseDB.CUSTOM_TOPICS.select(
9+
"topicName"
10+
)
11+
.eq("topicName", topicName)
12+
.maybeSingle();
13+
14+
if (!existing) {
15+
await SupabaseDB.CUSTOM_TOPICS.insert({ topicName }).throwOnError();
16+
console.log(`Created custom topic in Supabase: ${topicName}`);
17+
}
18+
19+
// Find all staff userIds from authRoles
20+
const { data: staffRoles, error: staffErr } =
21+
await SupabaseDB.AUTH_ROLES.select("userId")
22+
.eq("role", "STAFF")
23+
.throwOnError();
24+
if (staffErr) throw staffErr;
25+
26+
const staffUserIds = (staffRoles ?? []).map(
27+
(r: { userId: string }) => r.userId
28+
);
29+
if (staffUserIds.length === 0) {
30+
console.log("No staff userIds found. Nothing to subscribe.");
31+
process.exit(0);
32+
}
33+
34+
const { data: userDevices } = await SupabaseDB.NOTIFICATIONS.select(
35+
"userId, deviceId"
36+
)
37+
.in("userId", staffUserIds)
38+
.throwOnError();
39+
40+
const deviceTokens = (userDevices ?? [])
41+
.map((d: { deviceId: string | null }) => d.deviceId)
42+
.filter((t: string | null): t is string => Boolean(t));
43+
44+
if (deviceTokens.length === 0) {
45+
console.log("No device tokens found for staff. Exiting.");
46+
process.exit(0);
47+
}
48+
49+
const admin = getFirebaseAdmin();
50+
await admin.messaging().subscribeToTopic(deviceTokens, topicName); // we have like 50 people on staff
51+
console.log(
52+
`Done. Subscribed ${deviceTokens.length} device(s) for ${staffUserIds.length} staff.`
53+
);
54+
}
55+
56+
main().catch((err) => {
57+
console.error(err);
58+
process.exit(1);
59+
});

src/services/subscription/subscription-router.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,20 @@ subscriptionRouter.post(
8989
});
9090
}
9191

92-
// Get email addresses for all subscribed users
92+
// need to batch to avoid URL length limits
9393
const userIds = subscriptions.map((sub) => sub.userId);
94-
const { data: users } = await SupabaseDB.AUTH_INFO.select("email")
95-
.in("userId", userIds)
96-
.throwOnError();
94+
const BATCH_SIZE = 100;
95+
const emailAddresses: string[] = [];
96+
97+
for (let i = 0; i < userIds.length; i += BATCH_SIZE) {
98+
const batch = userIds.slice(i, i + BATCH_SIZE);
99+
const { data: users } = await SupabaseDB.AUTH_INFO.select("email")
100+
.in("userId", batch)
101+
.throwOnError();
97102

98-
const emailAddresses = users?.map((user) => user.email) || [];
103+
const batchEmails = users?.map((user) => user.email) || [];
104+
emailAddresses.push(...batchEmails);
105+
}
99106

100107
const sendEmailCommand = new SendEmailCommand({
101108
FromEmailAddress: Config.FROM_EMAIL_ADDRESS ?? "",
@@ -168,13 +175,20 @@ subscriptionRouter.get(
168175
.json({ error: "No subscribers found for this mailing list." });
169176
}
170177

171-
// Get email addresses for all subscribed users
178+
// Get email addresses for all subscribed users (batch to avoid URL length limits)
172179
const userIds = subscriptions.map((sub) => sub.userId);
173-
const { data: users } = await SupabaseDB.AUTH_INFO.select("email")
174-
.in("userId", userIds)
175-
.throwOnError();
180+
const BATCH_SIZE = 100; // Process in smaller batches
181+
const emailAddresses: string[] = [];
182+
183+
for (let i = 0; i < userIds.length; i += BATCH_SIZE) {
184+
const batch = userIds.slice(i, i + BATCH_SIZE);
185+
const { data: users } = await SupabaseDB.AUTH_INFO.select("email")
186+
.in("userId", batch)
187+
.throwOnError();
176188

177-
const emailAddresses = users?.map((user) => user.email) || [];
189+
const batchEmails = users?.map((user) => user.email) || [];
190+
emailAddresses.push(...batchEmails);
191+
}
178192

179193
return res.status(StatusCodes.OK).json(emailAddresses);
180194
}

0 commit comments

Comments
 (0)