Skip to content

Commit f60c5bc

Browse files
committed
feat(server): add per-user EventGrid webhook endpoint with subscription validation
1 parent 882f5a7 commit f60c5bc

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

server/src/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,49 @@ app.post("/api/logout", (_req: Request, res: Response) => {
3535
res.json({ ok: true });
3636
});
3737

38+
// Event Grid webhook for per-user webhook paths.
39+
// Example: POST /api/webhook/<user-unique-path>
40+
// If WEBHOOK_ALLOWED_KEYS is set (comma-separated), the :userPath must be present there.
41+
app.post("/api/webhook/:userPath", (req: Request, res: Response) => {
42+
const userPath = req.params.userPath;
43+
44+
// Optional allow-list: comma separated keys in env var WEBHOOK_ALLOWED_KEYS
45+
const allowed = process.env.WEBHOOK_ALLOWED_KEYS;
46+
if (allowed) {
47+
const allowedSet = new Set(allowed.split(",").map(s => s.trim()).filter(Boolean));
48+
if (!allowedSet.has(userPath)) {
49+
console.warn(`Rejected webhook for unknown key: ${userPath}`);
50+
return res.status(404).json({ error: "unknown webhook path" });
51+
}
52+
}
53+
54+
// Event Grid sends an array of events in the body
55+
const events = Array.isArray(req.body) ? req.body : [req.body];
56+
57+
// Subscription validation event
58+
const validationEvent = events.find((e: any) => e && e.eventType === 'Microsoft.EventGrid.SubscriptionValidationEvent');
59+
if (validationEvent) {
60+
const data = validationEvent.data || {};
61+
console.log(`EventGrid subscription validation for ${userPath}:`, data);
62+
// Reply with validationResponse per Event Grid requirement
63+
return res.json({ validationResponse: data.validationCode });
64+
}
65+
66+
// Normal events: log and ack
67+
try {
68+
console.log(`Received ${events.length} event(s) for webhook ${userPath}`);
69+
// For debugging include a small sample of events
70+
console.log(JSON.stringify(events.map((e: any) => ({ id: e.id, eventType: e.eventType })), null, 2));
71+
} catch (err) {
72+
console.warn('Failed to log events', (err as Error).message);
73+
}
74+
75+
// TODO: integrate with persistence / user properties: look up which user has this key
76+
// and forward/enqueue events appropriately.
77+
78+
return res.status(200).json({ received: events.length });
79+
});
80+
3881
// Serve static frontend if present in the final image at '../editor-dist'
3982
const staticPath = path.join(__dirname, "..", "editor-dist");
4083
const FRONTEND_INDEX = path.join(staticPath, "index.html");

0 commit comments

Comments
 (0)