Skip to content

Commit ee96f34

Browse files
committed
refactor(email): Create nodemailer package
1 parent 8f03897 commit ee96f34

File tree

23 files changed

+329
-82
lines changed

23 files changed

+329
-82
lines changed

.github/workflows/bump_publish.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,21 @@ jobs:
8282

8383
- name: Publish canary
8484
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'canary' }}
85-
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag canary --no-git-checks --access public
85+
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --tag canary --no-git-checks --access public
8686
env:
8787
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
8888
NPM_CONFIG_PROVENANCE: true
8989

9090
- name: Publish release candidate
9191
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'release-candidate' }}
92-
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag rc --no-git-checks --access public
92+
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --tag rc --no-git-checks --access public
9393
env:
9494
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
9595
NPM_CONFIG_PROVENANCE: true
9696

9797
- name: Publish stable
9898
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'stable' }}
99-
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag latest --no-git-checks --access public
99+
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --tag latest --no-git-checks --access public
100100
env:
101101
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
102102
NPM_CONFIG_PROVENANCE: true

apps/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@types/react": "^19.2.2",
3434
"@types/react-dom": "^19.2.2",
3535
"@vitnode/config": "workspace:*",
36+
"@vitnode/nodemailer": "workspace:*",
3637
"dotenv": "^17.2.3",
3738
"eslint": "^9.39.1",
3839
"react-email": "^5.0.1",

apps/api/src/vitnode.api.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { NodemailerEmailAdapter } from "@vitnode/core/api/adapters/email/nodemailer";
21
import { buildApiConfig } from "@vitnode/core/vitnode.config";
2+
import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
33
import { config } from "dotenv";
44
import { drizzle } from "drizzle-orm/postgres-js";
55

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: Nodemailer (SMTP)
3+
description: Send emails using SMTP with the Nodemailer adapter.
4+
---
5+
6+
| Cloud | Self-Hosted | Links |
7+
| ---------------- | ------------ | ------------------------------------------------------- |
8+
| ❌ Not Supported | ✅ Supported | [NPM Package](https://www.npmjs.com/package/nodemailer) |
9+
10+
## Usage
11+
12+
<Steps>
13+
<Step>
14+
### Installation
15+
16+
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
17+
18+
<Tabs groupId='package-manager' persist items={['bun', 'pnpm', 'npm']} label='Install nodemailer adapter'>
19+
20+
```bash tab="bun"
21+
bun i @vitnode/nodemailer -D
22+
```
23+
24+
```bash tab="pnpm"
25+
pnpm i @vitnode/nodemailer -D
26+
```
27+
28+
```bash tab="npm"
29+
npm i @vitnode/nodemailer -D
30+
```
31+
32+
</Tabs>
33+
34+
</Step>
35+
<Step>
36+
### Import the adapter
37+
38+
```ts title="vitnode.api.config.ts"
39+
// [!code ++]
40+
import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
41+
import { buildApiConfig } from "@vitnode/core/vitnode.config";
42+
43+
export const vitNodeApiConfig = buildApiConfig({
44+
email: {
45+
// [!code ++:6]
46+
adapter: NodemailerEmailAdapter({
47+
from: process.env.NODE_MAILER_FROM,
48+
host: process.env.NODE_MAILER_HOST,
49+
password: process.env.NODE_MAILER_PASSWORD,
50+
user: process.env.NOD_EMAILER_USER,
51+
}),
52+
},
53+
});
54+
```
55+
56+
</Step>
57+
<Step>
58+
59+
### Environment Variables
60+
61+
Add the following environment variables to your `.env` file:
62+
63+
```bash title=".env"
64+
NODE_MAILER_FROM=xxx
65+
NODE_MAILER_HOST=xxx
66+
NODE_MAILER_PASSWORD=xxx
67+
NOD_EMAILER_USER=xxx
68+
```
69+
70+
</Step>
71+
</Steps>

apps/docs/content/docs/dev/email/smtp.mdx

Lines changed: 0 additions & 13 deletions
This file was deleted.

apps/docs/eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import eslintVitNode from "@vitnode/config/eslint";
2+
import eslintVitNodeReact from "@vitnode/config/eslint.react";
23
import { fileURLToPath } from "node:url";
34
import { dirname } from "node:path";
45

56
const __dirname = dirname(fileURLToPath(import.meta.url));
67

78
export default [
89
...eslintVitNode,
10+
...eslintVitNodeReact,
911
{
1012
ignores: [".source"],
1113
},

apps/docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@types/react": "^19.2.2",
5454
"@types/react-dom": "^19.2.2",
5555
"@vitnode/config": "workspace:*",
56+
"@vitnode/nodemailer": "workspace:*",
5657
"babel-plugin-react-compiler": "^1.0.0",
5758
"class-variance-authority": "^0.7.1",
5859
"eslint": "^9.39.1",

apps/docs/src/vitnode.api.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { blogApiPlugin } from "@vitnode/blog/config.api";
22
import { NodeCronAdapter } from "@vitnode/core/api/adapters/cron/node-cron.adapter";
3-
import { NodemailerEmailAdapter } from "@vitnode/core/api/adapters/email/nodemailer";
3+
4+
import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
45
import { DiscordSSOApiPlugin } from "@vitnode/core/api/adapters/sso/discord";
56
import { FacebookSSOApiPlugin } from "@vitnode/core/api/adapters/sso/facebook";
67
import { GoogleSSOApiPlugin } from "@vitnode/core/api/adapters/sso/google";

packages/config/eslint.config.mjs

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
// @ts-check
22

3-
import { dirname } from "node:path";
4-
import { fileURLToPath } from "node:url";
53
import eslint from "@eslint/js";
6-
import eslintReact from "@eslint-react/eslint-plugin";
7-
import jsxA11y from "eslint-plugin-jsx-a11y";
84
import perfectionist from "eslint-plugin-perfectionist";
95
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
10-
import reactPlugin from "eslint-plugin-react";
11-
import hooksPlugin from "eslint-plugin-react-hooks";
126
import tsEslint from "typescript-eslint";
13-
import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect";
14-
15-
const __dirname = dirname(fileURLToPath(import.meta.url));
167

178
export default [
18-
reactYouMightNotNeedAnEffect.configs.recommended,
199
{
2010
ignores: [
2111
"next-env.d.ts",
@@ -36,43 +26,13 @@ export default [
3626
],
3727
},
3828
eslint.configs.recommended,
39-
eslintReact.configs.recommended,
4029
...tsEslint.configs.stylisticTypeChecked,
4130
...tsEslint.configs.strictTypeChecked,
4231
eslintPluginPrettierRecommended,
43-
jsxA11y.flatConfigs.recommended,
44-
reactPlugin.configs.flat.recommended,
4532
perfectionist.configs["recommended-natural"],
46-
{
47-
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
48-
settings: {
49-
react: {
50-
version: "detect",
51-
},
52-
},
53-
languageOptions: {
54-
parserOptions: {
55-
ecmaFeatures: {
56-
jsx: true,
57-
},
58-
},
59-
},
60-
},
61-
{
62-
plugins: {
63-
"react-hooks": hooksPlugin,
64-
},
65-
rules: {
66-
"react/react-in-jsx-scope": "off",
67-
...hooksPlugin.configs.recommended.rules,
68-
},
69-
},
7033
{ files: ["**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}"] },
7134
{
7235
rules: {
73-
"react-hooks/exhaustive-deps": "off",
74-
"@eslint-react/no-context-provider": "off",
75-
"@eslint-react/no-unstable-default-props": "off",
7636
"perfectionist/sort-array-includes": "warn",
7737
"@typescript-eslint/consistent-type-imports": "error",
7838
"@typescript-eslint/no-confusing-void-expression": "off",
@@ -143,30 +103,10 @@ export default [
143103
"newline-before-return": "warn",
144104
"no-restricted-imports": [
145105
"error",
146-
{
147-
name: "next/link",
148-
message: "Please import from `vitnode-frontend/navigation` instead.",
149-
},
150106
{
151107
name: "drizzle-orm/mysql-core",
152108
message: "Please import from `drizzle-orm/pg-core` instead.",
153109
},
154-
{
155-
name: "next/navigation",
156-
importNames: [
157-
"redirect",
158-
"permanentRedirect",
159-
"useRouter",
160-
"usePathname",
161-
],
162-
message: "Please import from `vitnode-frontend/navigation` instead.",
163-
},
164-
{
165-
name: "next/router",
166-
importNames: ["useRouter"],
167-
message:
168-
"This import is from Page router. Please import from `vitnode-frontend/navigation` instead.",
169-
},
170110
],
171111
},
172112
},
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// @ts-check
2+
3+
import eslintReact from "@eslint-react/eslint-plugin";
4+
import reactPlugin from "eslint-plugin-react";
5+
import hooksPlugin from "eslint-plugin-react-hooks";
6+
import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect";
7+
8+
export default [
9+
reactYouMightNotNeedAnEffect.configs.recommended,
10+
eslintReact.configs.recommended,
11+
reactPlugin.configs.flat.recommended,
12+
{
13+
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
14+
settings: {
15+
react: {
16+
version: "detect",
17+
},
18+
},
19+
languageOptions: {
20+
parserOptions: {
21+
ecmaFeatures: {
22+
jsx: true,
23+
},
24+
},
25+
},
26+
},
27+
{
28+
plugins: {
29+
"react-hooks": hooksPlugin,
30+
},
31+
rules: {
32+
"react/react-in-jsx-scope": "off",
33+
...hooksPlugin.configs.recommended.rules,
34+
},
35+
},
36+
{ files: ["**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}"] },
37+
{
38+
rules: {
39+
"react-hooks/exhaustive-deps": "off",
40+
"@eslint-react/no-context-provider": "off",
41+
"@eslint-react/no-unstable-default-props": "off",
42+
"no-restricted-imports": [
43+
"error",
44+
{
45+
name: "next/link",
46+
message: "Please import from `vitnode-frontend/navigation` instead.",
47+
},
48+
{
49+
name: "next/navigation",
50+
importNames: [
51+
"redirect",
52+
"permanentRedirect",
53+
"useRouter",
54+
"usePathname",
55+
],
56+
message: "Please import from `vitnode-frontend/navigation` instead.",
57+
},
58+
{
59+
name: "next/router",
60+
importNames: ["useRouter"],
61+
message:
62+
"This import is from Page router. Please import from `vitnode-frontend/navigation` instead.",
63+
},
64+
],
65+
},
66+
},
67+
];

0 commit comments

Comments
 (0)