example: adds email example
This commit is contained in:
4
examples/email/.env.example
Normal file
4
examples/email/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
MONGODB_URI=mongodb://localhost/payload-example-email
|
||||
PAYLOAD_SECRET=
|
||||
NODE_ENV=development
|
||||
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:8000
|
||||
7
examples/email/.eslintrc.js
Normal file
7
examples/email/.eslintrc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@payloadcms'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
},
|
||||
}
|
||||
5
examples/email/.gitignore
vendored
Normal file
5
examples/email/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
build
|
||||
dist
|
||||
node_modules
|
||||
package-lock.json
|
||||
.env
|
||||
63
examples/email/README.md
Normal file
63
examples/email/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Payload Email Example
|
||||
|
||||
This example demonstrates how to integrate email functionality into Payload.
|
||||
|
||||
## Quick Start
|
||||
|
||||
To spin up this example locally, follow these steps:
|
||||
|
||||
1. Clone this repo
|
||||
2. `cd` into this directory and run `yarn` or `npm install`
|
||||
3. `cp .env.example .env` to copy the example environment variables
|
||||
4. `yarn dev` or `npm run dev` to start the server and seed the database
|
||||
5. `open http://localhost:8000/admin` to access the admin panel
|
||||
6. Create your first user
|
||||
|
||||
## How it works
|
||||
|
||||
Payload utilizes [NodeMailer](https://nodemailer.com/about/) for email functionality. Once you add your email configuration to `payload.init()`, you send email from anywhere in your application just by calling `payload.sendEmail({})`.
|
||||
|
||||
1. Navigate to `src/server.ts` - this is where your email config gets passed to Payload
|
||||
2. Open `src/email/transport.ts` - here we are defining the email config. You can use an env variable to switch between the mock email transport and live email service.
|
||||
|
||||
Now we can start sending email!
|
||||
|
||||
3. Go to `src/collections/Newsletter.ts` - with an `afterChange` hook, we are sending an email when a new user signs up for the newsletter
|
||||
|
||||
Let's not forget our authentication emails...
|
||||
|
||||
4. Auth-enabled collections have built-in options to verify the user and reset the user password. Open `src/collections/Users.ts` and see how we customize these emails.
|
||||
|
||||
Speaking of customization...
|
||||
|
||||
5. Take a look at `src/email/generateEmailHTML` and how it compiles a custom template when sending email. You change this to any HTML template of your choosing.
|
||||
|
||||
That's all you need, now you can go ahead and test out this repo by creating a new `user` or `newsletter-signup` and see the email integration in action.
|
||||
|
||||
## Development
|
||||
|
||||
To spin up this example locally, follow the [Quick Start](#quick-start).
|
||||
|
||||
## Production
|
||||
|
||||
To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps:
|
||||
|
||||
1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle.
|
||||
1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory.
|
||||
|
||||
### Deployment
|
||||
|
||||
The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details.
|
||||
|
||||
## Resources
|
||||
|
||||
For more information on integrating email, check out these resources:
|
||||
|
||||
<!-- Update with live blog post URL when published -->
|
||||
|
||||
- [Blog Post - Email 101](https://payloadcms.com/blog)
|
||||
- [Email Documentation](https://payloadcms.com/docs/email/overview#email-functionality)
|
||||
|
||||
## Questions
|
||||
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/r6sCXqVk3v) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
4
examples/email/nodemon.json
Normal file
4
examples/email/nodemon.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts"
|
||||
}
|
||||
35
examples/email/package.json
Normal file
35
examples/email/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "payload-example-email",
|
||||
"description": "Payload Email integration example.",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn copyfiles && yarn build:payload && yarn build:server",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
|
||||
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "1.6.29",
|
||||
"handlebars": "^4.7.7",
|
||||
"inline-css": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.19.0",
|
||||
"nodemon": "^2.0.6",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
39
examples/email/src/collections/Newsletter.ts
Normal file
39
examples/email/src/collections/Newsletter.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
import generateEmailHTML from '../email/generateEmailHTML'
|
||||
|
||||
const Newsletter: CollectionConfig = {
|
||||
slug: 'newsletter-signups',
|
||||
admin: {
|
||||
defaultColumns: ['name', 'email'],
|
||||
},
|
||||
hooks: {
|
||||
afterChange: [
|
||||
async ({ doc, operation, req }) => {
|
||||
if (operation === 'create') {
|
||||
req.payload.sendEmail({
|
||||
to: doc.email,
|
||||
from: 'sender@example.com',
|
||||
subject: 'Thanks for signing up!',
|
||||
html: await generateEmailHTML({
|
||||
headline: 'Welcome to the newsletter!',
|
||||
content: `<p>${doc.name ? `Hi ${doc.name}!` : 'Hi!'} We'll be in touch soon...</p>`,
|
||||
}),
|
||||
})
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
export default Newsletter
|
||||
29
examples/email/src/collections/Users.ts
Normal file
29
examples/email/src/collections/Users.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import generateForgotPasswordEmail from '../email/generateForgotPasswordEmail'
|
||||
import generateVerificationEmail from '../email/generateVerificationEmail'
|
||||
|
||||
const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: {
|
||||
verify: {
|
||||
generateEmailSubject: () => 'Verify your email',
|
||||
generateEmailHTML: generateVerificationEmail,
|
||||
},
|
||||
forgotPassword: {
|
||||
generateEmailSubject: () => 'Reset your password',
|
||||
generateEmailHTML: generateForgotPasswordEmail,
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default Users
|
||||
24
examples/email/src/email/generateEmailHTML.ts
Normal file
24
examples/email/src/email/generateEmailHTML.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import fs from 'fs'
|
||||
import Handlebars from 'handlebars'
|
||||
import inlineCSS from 'inline-css'
|
||||
import path from 'path'
|
||||
|
||||
const template = fs.readFileSync
|
||||
? fs.readFileSync(path.join(__dirname, './template.html'), 'utf8')
|
||||
: ''
|
||||
|
||||
// Compile the template
|
||||
const getHTML = Handlebars.compile(template)
|
||||
|
||||
const generateEmailHTML = async (data): Promise<string> => {
|
||||
const preInlinedCSS = getHTML(data)
|
||||
|
||||
const html = await inlineCSS(preInlinedCSS, {
|
||||
url: ' ',
|
||||
removeStyleTags: false,
|
||||
})
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
export default generateEmailHTML
|
||||
14
examples/email/src/email/generateForgotPasswordEmail.ts
Normal file
14
examples/email/src/email/generateForgotPasswordEmail.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import generateEmailHTML from './generateEmailHTML'
|
||||
|
||||
const generateForgotPasswordEmail = async ({ token }): Promise<string> =>
|
||||
generateEmailHTML({
|
||||
headline: 'Locked out?',
|
||||
content:
|
||||
'<p>Let's get you back in.</p>',
|
||||
cta: {
|
||||
buttonLabel: 'Reset your password',
|
||||
url: `${process.env.PAYLOAD_PUBLIC_SERVER_URL}/reset-password?token=${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
export default generateForgotPasswordEmail
|
||||
16
examples/email/src/email/generateVerificationEmail.ts
Normal file
16
examples/email/src/email/generateVerificationEmail.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import generateEmailHTML from './generateEmailHTML'
|
||||
|
||||
const generateVerificationEmail = async (args): Promise<string> => {
|
||||
const { user, token } = args
|
||||
|
||||
return generateEmailHTML({
|
||||
headline: 'Verify your account',
|
||||
content: `<p>Hi${user.name ? ' ' + user.name : ''}! Validate your account by clicking the button below.</p>`,
|
||||
cta: {
|
||||
buttonLabel: 'Verify',
|
||||
url: `${process.env.PAYLOAD_PUBLIC_SERVER_URL}/verify?token=${token}&email=${user.email}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default generateVerificationEmail
|
||||
317
examples/email/src/email/template.html
Normal file
317
examples/email/src/email/template.html
Normal file
@@ -0,0 +1,317 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style type="text/css">
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
html,
|
||||
.bg {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p,
|
||||
em,
|
||||
strong {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 15px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #333333;
|
||||
outline: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 25px;
|
||||
color: #333333;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
color: #333333;
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: #333333;
|
||||
font-size: 17px;
|
||||
font-weight: 900;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
p,
|
||||
td {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 25px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 15px;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
table.hr td {
|
||||
font-size: 0;
|
||||
line-height: 2px;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAIN
|
||||
********************************/
|
||||
|
||||
.main {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/********************************
|
||||
MAX WIDTHS
|
||||
********************************/
|
||||
|
||||
.max-width {
|
||||
max-width: 800px;
|
||||
width: 94%;
|
||||
margin: 0 3%;
|
||||
}
|
||||
|
||||
/********************************
|
||||
REUSABLES
|
||||
********************************/
|
||||
|
||||
.padding {
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: 0;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
line-height: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
/********************************
|
||||
PANELS
|
||||
********************************/
|
||||
|
||||
.panel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width : 800px) {
|
||||
|
||||
h1 {
|
||||
font-size: 24px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px !important;
|
||||
margin: 0 0 20px 0 !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px !important;
|
||||
margin: 0 0 15px 0 !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 15px !important;
|
||||
margin: 0 0 10px !important;
|
||||
}
|
||||
|
||||
.max-width {
|
||||
width: 90% !important;
|
||||
margin: 0 5% !important;
|
||||
}
|
||||
|
||||
td.padding {
|
||||
padding: 30px !important;
|
||||
}
|
||||
|
||||
td.padding-vert {
|
||||
padding-top: 20px !important;
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
|
||||
td.padding-horiz {
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
line-height: 20px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color:#F3F3F3; height: 100%;">
|
||||
<table height="100%" width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f3f3f3"
|
||||
style="background-color: #f3f3f3;">
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="max-width" cellpadding="0" cellspacing="0" border="0" width="100%"
|
||||
style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding main">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- LOGO -->
|
||||
<a href="https://payloadcms.com/" target="_blank">
|
||||
<img src="https://payloadcms.com/images/logo-dark.png" width="150"
|
||||
height="auto" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="spacer"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- HEADLINE -->
|
||||
<h1 style="margin: 0 0 30px;">{{headline}}</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- CONTENT -->
|
||||
{{{content}}}
|
||||
|
||||
<!-- CTA -->
|
||||
{{#if cta}}
|
||||
<div>
|
||||
<a href="{{cta.url}}"
|
||||
style="background-color:#222222;border-radius:4px;color:#ffffff;display:inline-block;font-family:sans-serif;font-size:13px;font-weight:bold;line-height:60px;text-align:center;text-decoration:none;width:200px;-webkit-text-size-adjust:none;">
|
||||
{{cta.buttonLabel}}
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
20
examples/email/src/email/transport.ts
Normal file
20
examples/email/src/email/transport.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
let email
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
email = {
|
||||
fromName: 'Payload',
|
||||
fromAddress: 'info@payloadcms.com',
|
||||
transportOptions: {
|
||||
// Configure a custom transport here
|
||||
},
|
||||
}
|
||||
|
||||
} else {
|
||||
email = {
|
||||
fromName: 'Ethereal Email',
|
||||
fromAddress: 'example@ethereal.com',
|
||||
logMockCredentials: true,
|
||||
}
|
||||
}
|
||||
|
||||
export default email
|
||||
1
examples/email/src/emptyModule.js
Normal file
1
examples/email/src/emptyModule.js
Normal file
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
35
examples/email/src/payload-types.ts
Normal file
35
examples/email/src/payload-types.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/* tslint:disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload CMS.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
collections: {
|
||||
'newsletter-signups': NewsletterSignup;
|
||||
users: User;
|
||||
};
|
||||
globals: {};
|
||||
}
|
||||
export interface NewsletterSignup {
|
||||
id: string;
|
||||
name?: string;
|
||||
email: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
_verified?: boolean;
|
||||
_verificationToken?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
password?: string;
|
||||
}
|
||||
46
examples/email/src/payload.config.ts
Normal file
46
examples/email/src/payload.config.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
import Users from './collections/Users'
|
||||
import Newsletter from './collections/Newsletter'
|
||||
|
||||
dotenv.config({
|
||||
path: path.resolve(__dirname, '../.env'),
|
||||
})
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, './emptyModule.js')
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
webpack: config => ({
|
||||
...config,
|
||||
resolve: {
|
||||
...config?.resolve,
|
||||
alias: [
|
||||
'fs',
|
||||
'handlebars',
|
||||
'inline-css',
|
||||
path.resolve(__dirname, './email/transport'),
|
||||
path.resolve(__dirname, './email/generateEmailHTML'),
|
||||
path.resolve(__dirname, './email/generateForgotPasswordEmail'),
|
||||
path.resolve(__dirname, './email/generateVerificationEmail'),
|
||||
].reduce(
|
||||
(aliases, importPath) => ({
|
||||
...aliases,
|
||||
[importPath]: mockModulePath,
|
||||
}),
|
||||
config.resolve.alias,
|
||||
),
|
||||
},
|
||||
}),
|
||||
},
|
||||
collections: [
|
||||
Newsletter,
|
||||
Users,
|
||||
],
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
30
examples/email/src/server.ts
Normal file
30
examples/email/src/server.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import payload from 'payload'
|
||||
import email from './email/transport'
|
||||
|
||||
require('dotenv').config({
|
||||
path: path.resolve(__dirname, '../.env'),
|
||||
})
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get('/', (_, res) => {
|
||||
res.redirect('/admin')
|
||||
})
|
||||
|
||||
const start = async (): Promise<void> => {
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
email,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
|
||||
},
|
||||
})
|
||||
|
||||
app.listen(8000)
|
||||
}
|
||||
|
||||
start()
|
||||
38
examples/email/tsconfig.json
Normal file
38
examples/email/tsconfig.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"jsx": "react",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"payload/generated-types": [
|
||||
"./src/payload-types.ts"
|
||||
],
|
||||
"node_modules/*": [
|
||||
"./node_modules/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
6919
examples/email/yarn.lock
Normal file
6919
examples/email/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user