Riyadh b54d4e35d9 Send executive-meeting notification emails for real (SMTP delivery)
Original task #163: replace the no-op outbox-log behaviour in
`sendExecutiveMeetingEmail` with real SMTP delivery so approvers
actually get pinged in their inbox when an executive-meeting request
is awaiting review.

Implementation
- Added `nodemailer` (and `@types/nodemailer`) as a dependency of
  `@workspace/api-server`. nodemailer was already in the build's
  external list, so the bundle stays slim and resolves it at runtime.
- Rewrote `sendExecutiveMeetingEmail` in
  `artifacts/api-server/src/lib/executive-meeting-notify.ts`:
  - Lazily builds a cached nodemailer transporter from
    `SMTP_HOST` / `SMTP_PORT` (default 587) / `SMTP_USER` /
    `SMTP_PASS`, with optional `SMTP_SECURE` and `SMTP_FROM`.
    Cache is keyed on a config signature, so changing env vars in
    tests / hot reloads naturally rebuilds the transporter.
  - When `SMTP_HOST` is unset the previous outbox-style log is kept
    verbatim as a fallback. Once `SMTP_HOST` is set the fallback
    branch is no longer reached.
  - Picks subject/body in the recipient's preferred language
    (Arabic vs English) with a sensible fallback.
  - Sends one mail per addressed recipient in parallel; per-recipient
    failures are caught and logged at warn level. The outer
    try/catch keeps the function from ever throwing into the
    transaction caller. SMTP `rejected` arrays are also logged at
    warn so bounces are visible.

Code-review comment follow-ups (applied in this commit)
- Transport cache signature now hashes `SMTP_PASS` (sha256, first
  16 hex chars) instead of just "***", so rotating to a new password
  actually rebuilds the cached transporter without leaking plaintext.
- Fallback log message now distinguishes "no SMTP_HOST configured"
  from "SMTP misconfigured" so operators can tell why a delivery
  was skipped.

Verification
- Typecheck passes for the modified file (other unrelated pre-existing
  typecheck failures remain).
- `pnpm --filter @workspace/api-server run build` succeeds.
- API server boots cleanly with the new dependency.

Deviations / scope
- No automated tests added; the existing test harness is integration-
  style and the project already tracks "Add automated tests for the
  notification fan-out logic" as a separate task.
- Persisting per-recipient delivery state into the
  `executive_meeting_notifications` audit table and a startup
  `transporter.verify()` were intentionally deferred and proposed as
  follow-ups (#232 and #233).
2026-04-30 18:29:43 +00:00
2026-04-21 12:16:59 +00:00
2026-04-18 02:00:09 +00:00
2026-04-18 02:00:09 +00:00
S
Description
No description provided
139 MiB
Languages
TypeScript 69.4%
JavaScript 29%
CSS 0.6%
Shell 0.6%
Dockerfile 0.2%
Other 0.2%