b54d4e35d9
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).
55 lines
1.6 KiB
JSON
55 lines
1.6 KiB
JSON
{
|
|
"name": "@workspace/api-server",
|
|
"version": "0.0.0",
|
|
"private": true,
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "export NODE_ENV=development && pnpm run build && pnpm run start",
|
|
"build": "node ./build.mjs",
|
|
"start": "node --enable-source-maps ./dist/index.mjs",
|
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
"test:wait": "node ./scripts/wait-for-server.mjs",
|
|
"test": "node --test 'tests/**/*.test.mjs'"
|
|
},
|
|
"dependencies": {
|
|
"@google-cloud/storage": "^7.19.0",
|
|
"@swc/helpers": "^0.5.21",
|
|
"@workspace/api-zod": "workspace:*",
|
|
"@workspace/db": "workspace:*",
|
|
"bcryptjs": "^3.0.3",
|
|
"bidi-js": "^1.0.3",
|
|
"connect-pg-simple": "^10.0.0",
|
|
"cookie-parser": "^1.4.7",
|
|
"cors": "^2",
|
|
"drizzle-orm": "catalog:",
|
|
"express": "^5",
|
|
"express-session": "^1.19.0",
|
|
"google-auth-library": "^10.6.2",
|
|
"nodemailer": "^8.0.7",
|
|
"pdfkit": "^0.18.0",
|
|
"pino": "^9",
|
|
"pino-http": "^10",
|
|
"sanitize-html": "^2.17.3",
|
|
"socket.io": "^4.8.3",
|
|
"zod": "catalog:"
|
|
},
|
|
"devDependencies": {
|
|
"@types/bcryptjs": "^3.0.0",
|
|
"@types/connect-pg-simple": "^7.0.3",
|
|
"@types/cookie-parser": "^1.4.10",
|
|
"@types/cors": "^2.8.19",
|
|
"@types/express": "^5.0.6",
|
|
"@types/express-session": "^1.19.0",
|
|
"@types/node": "catalog:",
|
|
"@types/nodemailer": "^8.0.0",
|
|
"@types/pdfkit": "^0.17.6",
|
|
"@types/sanitize-html": "^2.16.1",
|
|
"esbuild": "^0.27.3",
|
|
"esbuild-plugin-pino": "^2.3.3",
|
|
"pg": "^8.20.0",
|
|
"pino-pretty": "^13",
|
|
"socket.io-client": "^4.8.3",
|
|
"thread-stream": "3.1.0"
|
|
}
|
|
}
|