feat(02-02): install nodemailer and create contact mailer + server action

- Add nodemailer@^7.0.7 and @types/nodemailer (v7 required by next-auth peer dep)
- Create src/lib/contact-mailer.ts: Nodemailer SMTP transporter + sendContactEmail()
- Create src/lib/contact-action.ts: Server Action with Zod validation, honeypot check
- SMTP credentials read from CONTACT_EMAIL_USER/PASS/SMTP_HOST/PORT env vars
- Add CONTACT_* placeholder vars to .env.local (gitignored, for local setup docs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chandler Copeland
2026-03-19 14:59:23 -06:00
parent 0cdf3e5f7d
commit 39f233dbb4
4 changed files with 113 additions and 0 deletions

View File

@@ -11,8 +11,10 @@
"@vercel/blob": "^2.3.1",
"bcryptjs": "^3.0.3",
"drizzle-orm": "^0.45.1",
"lucide-react": "^0.577.0",
"next": "16.2.0",
"next-auth": "5.0.0-beta.30",
"nodemailer": "^7.0.13",
"postgres": "^3.4.8",
"react": "19.2.4",
"react-dom": "19.2.4",
@@ -22,6 +24,7 @@
"@tailwindcss/postcss": "^4",
"@types/bcryptjs": "^2.4.6",
"@types/node": "^20",
"@types/nodemailer": "^7.0.11",
"@types/react": "^19",
"@types/react-dom": "^19",
"dotenv": "^17.3.1",
@@ -2521,6 +2524,16 @@
"undici-types": "~6.21.0"
}
},
"node_modules/@types/nodemailer": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.11.tgz",
"integrity": "sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/pg": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.18.0.tgz",
@@ -6138,6 +6151,15 @@
"yallist": "^3.0.2"
}
},
"node_modules/lucide-react": {
"version": "0.577.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz",
"integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -6387,6 +6409,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/nodemailer": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz",
"integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/oauth4webapi": {
"version": "3.8.5",
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.5.tgz",