Add embedded IMAP/SMTP mail for staff
Admins configure one mail server; each staff member gets their own mailbox login. Read, reply and compose against real IMAP/SMTP. IMAP and SMTP are raw TCP, so none of this can run in a browser — every operation is a TanStack Start server function. mail.functions.ts ships to the client bundle, so imapflow/nodemailer/mailparser and the crypto helpers are imported inside handlers, never at the top level. Verified that Nitro inlines all three into .output/server/_libs, since the Docker runner stage copies only .output and has no node_modules. Note this ties the app to the Node deployment: the default local build targets Cloudflare Workers, which cannot open IMAP sockets. Credential handling, since a mailbox password grants full read and send access to someone's mail: - user_mailboxes has RLS enabled, no policies, and SELECT revoked from anon and authenticated. Verified: teacher and admin both see zero rows and no ciphertext; only service_role can read it. The revoke is belt and braces — Supabase's default privileges had granted SELECT, leaving the table one stray policy away from leaking. - Passwords are sealed with AES-256-GCM using MAIL_CRED_KEY from .env.secret, so a database dump alone opens nothing. GCM also makes tampering fail the auth tag instead of decrypting to garbage. - Provisioning verifies credentials against the live IMAP server before storing them, so typos surface at setup rather than as a broken inbox. Message bodies render as plain text; sender HTML is never injected, which would execute sender-controlled markup and leak read receipts via tracking pixels. Mailboxes are limited to admins and teachers. Students are excluded deliberately — external mail for minors carries archiving, monitoring and consent obligations that should be chosen, not inherited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,8 +50,11 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"imapflow": "^1.5.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.575.0",
|
||||
"mailparser": "^3.9.14",
|
||||
"nodemailer": "^9.0.3",
|
||||
"react": "^19.2.0",
|
||||
"react-day-picker": "^9.14.0",
|
||||
"react-dom": "^19.2.0",
|
||||
@@ -69,7 +72,9 @@
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.32.0",
|
||||
"@lovable.dev/vite-tanstack-config": "2.6.4",
|
||||
"@types/mailparser": "^3.4.6",
|
||||
"@types/node": "^22.16.5",
|
||||
"@types/nodemailer": "^8.0.1",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
|
||||
Reference in New Issue
Block a user