Repository navigation
Board invites: send through the Brevo API, 15s timeout on all email - #106
Merged
Merged
Conversation
Scheduling a meeting hung forever: invites went out with nodemailer over SMTP (smtp-relay.brevo.com:587), Railway blocks outbound SMTP, and nothing timed out. - Invites now go through POST /v3/smtp/email like the other board email, with the event attached as invite.ics (base64). The .ics is built exactly as before: METHOD:REQUEST/CANCEL, UID, SEQUENCE, RRULE, RECURRENCE-ID and VTIMEZONE. The API has no attachment content-type field; Brevo types it from the .ics extension. - nodemailer and the Brevo SDK are removed. lib/brevo.ts calls the API with fetch and gives up after 15 seconds (EMAIL_TIMEOUT_MS), for every email the site sends. A batch of invites or announcements stops at the first timeout instead of waiting 15 seconds per person. - inviteSetupProblem needs only BREVO_API_KEY and a sender. - board_scheduled.invites_sent counts invites that went through. If none did, the request returns "The invites didn't go through. Try again." and the Meetings tab shows the meeting as "Invites not sent" with Resend invites and Delete (which sends nothing). Cancellations go out before the meeting changes, so a failed cancel can be retried. - BOARD_INVITE_OUTBOX writes each API request (.json) and its .ics to the folder instead of sending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The bug
Scheduling a board meeting on the Meetings tab (both hubs) hung forever.
lib/board/invites.tssent invites with nodemailer over SMTP (smtp-relay.brevo.com:587), Railway blocks outbound SMTP on our plan, and nothing timed out, so the request never returned. The meeting row was saved before the send, so earlier attempts left meetings whose invites never went out.The fix
Invites go through the Brevo HTTP API. They use
POST https://api.brevo.com/v3/smtp/emailwithBREVO_API_KEY, the same as announcements and submissions. Each email has the HTML body (plus a plain-text version) and the event attached asinvite.ics(base64). The .ics comes from the unchangedlib/board/ics.ts, so METHOD:REQUEST / METHOD:CANCEL, UID, SEQUENCE, RRULE, RECURRENCE-ID and VTIMEZONE are exactly as before.What Brevo allows:
.icsis on Brevo's list of allowed attachment extensions, per the API reference text in the Brevo SDK (v5.0.3).content,nameorurl. There's no field for its content type, and Brevo sets the type from the file extension. So the API can't mark the attachmenttext/calendar; method=REQUEST/CANCELthe way nodemailer did. The method is still inside the .ics.invite.icsas an attachment to open rather than Yes / Maybe / No buttons. BOARD_SETUP.md says so.SMTP is gone.
@types/nodemailerand the Brevo SDK are removed; nothing else used them.inviteSetupProblemnow needs onlyBREVO_API_KEYand a sender (the chapter'sfrom, orBOARD_INVITE_FROM).BREVO_SMTP_LOGINandBREVO_SMTP_KEYare no longer used and are removed from.env.exampleand BOARD_SETUP.md.15-second timeout on every email.
lib/brevo.tsnow calls the API withfetchandAbortSignal.timeout(15000). That covers invites, announcements, submissions and the site's volunteer and contact forms.Meetings whose invites never went out.
board_scheduled.invites_sent, counts invites that went through. Existing rows start at 0, which matches the meetings saved while sending was broken.Outbox test mode.
BOARD_INVITE_OUTBOX=<folder>writes each API request as.json, with the decoded.icsbeside it, instead of sending.Tested (production build)
Outbox mode, with each .ics parsed by Python
icalendar:cancelled_datesThe same UID ran through every step, every file has CRLF line endings, and each email has an HTML and a text body.
Brevo hanging (the stand-in never answers):
invites_sent = 0In the browser:
invite.icsattached. The card now reads "2 invites sent".Lint (no errors; 4 existing warnings), typecheck and build pass.
Deploy notes
BREVO_SMTP_LOGINandBREVO_SMTP_KEYcan be deleted from Railway. Invites only needBREVO_API_KEYand the chapter senders.Generated by Claude Code