From 2f00a029367e4922674434120bee23353a27ca46 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 22 Mar 2016 22:09:22 -0700 Subject: [PATCH 1/8] Document email adapter --- README.md | 32 +++++++++- src/Adapters/Email/SimpleMailgunAdapter.js | 4 +- src/Controllers/UserController.js | 10 ++-- src/ParseServer.js | 69 +++++++++++----------- src/index.js | 6 +- 5 files changed, 75 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 5dea92337b..29f0030641 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ We have provided a basic [Node.js application](https://github.com/ParsePlatform/ * [Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-run-parse-server-on-ubuntu-14-04) * [NodeChef](https://nodechef.com/blog/post/6/migrate-from-parse-to-nodechef%E2%80%99s-managed-parse-server) * [Google App Engine](https://medium.com/@justinbeckwith/deploying-parse-server-to-google-app-engine-6bc0b7451d50) -* [Microsoft Azure](https://azure.microsoft.com/en-us/blog/azure-welcomes-parse-developers/) +* [Microsoft Azure](https://azure.microsoft.com/en-us/blog/azure-welcomes-parse-developers/) * [Pivotal Web Services](https://github.com/cf-platform-eng/pws-parse-server) * [Back4app](http://blog.back4app.com/2016/03/01/quick-wizard-migration/) @@ -187,6 +187,36 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo * `loggerAdapter` - The default behavior/transport (File) can be changed by creating an adapter class (see [`LoggerAdapter.js`](https://github.com/ParsePlatform/parse-server/blob/master/src/Adapters/Logger/LoggerAdapter.js)). * `databaseAdapter` - The backing store can be changed by creating an adapter class (see `DatabaseAdapter.js`). Defaults to `MongoStorageAdapter`. +##### Email verification and password reset + +Verifying user email addresses and enabling password reset via email requries an email adapter. As part of the `parse-server` package we provide an adapter for sending email through Mailgun. To use it, sign up for Mailgun, and add this to your initialization code: + +```js +var server = ParseServer({ + ...otherOptions, + // Enable email verification + verifyUserEmails: true, + // The public URL of your app. + // This will appear in the link that is used to verify email addresses and reset passwords. + publicServerURL: 'https://example.com', + // Your apps name. This will appear in the subject and body of the emails that are sent. + appName: 'Parse App', + // The email adapter + emailAdapter: { + module: 'parse-server/lib/Adapters/Email/SimpleMailgunAdapter', + options: { + // The addres that your emails come from + fromAddress: 'parse@example.com', + // Your domain from mailgun.com + domain: 'example.com', + // Your API key from mailgun.com + apiKey: 'key-mykey', + } + } +}); + +You can also use other email adapters contributed by the community such as [parse-server-sendgrid-adapter](https://www.npmjs.com/package/parse-server-sendgrid-adapter). + ### Using environment variables to configure Parse Server You may configure the Parse Server using environment variables: diff --git a/src/Adapters/Email/SimpleMailgunAdapter.js b/src/Adapters/Email/SimpleMailgunAdapter.js index a90a43d77b..f670429b63 100644 --- a/src/Adapters/Email/SimpleMailgunAdapter.js +++ b/src/Adapters/Email/SimpleMailgunAdapter.js @@ -1,8 +1,8 @@ import Mailgun from 'mailgun-js'; let SimpleMailgunAdapter = mailgunOptions => { - if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain) { - throw 'SimpleMailgunAdapter requires an API Key and domain.'; + if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain || !mailgunOptions.fromAddress) { + throw 'SimpleMailgunAdapter requires an API Key, domain, and fromAddress.'; } let mailgun = Mailgun(mailgunOptions); diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index 051ddebcff..76c7d3d3ab 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -1,8 +1,8 @@ -import { randomString } from '../cryptoUtils'; -import { inflate } from '../triggers'; +import { randomString } from '../cryptoUtils'; +import { inflate } from '../triggers'; import AdaptableController from './AdaptableController'; -import MailAdapter from '../Adapters/Email/MailAdapter'; -import rest from '../rest'; +import MailAdapter from '../Adapters/Email/MailAdapter'; +import rest from '../rest'; var DatabaseAdapter = require('../DatabaseAdapter'); var RestWrite = require('../RestWrite'); @@ -181,7 +181,7 @@ export class UserController extends AdaptableController { defaultVerificationEmail({link, user, appName, }) { let text = "Hi,\n\n" + - "You are being asked to confirm the e-mail address " + user.email + " with " + appName + "\n\n" + + "You are being asked to confirm the e-mail address " + user.get("email") + " with " + appName + "\n\n" + "" + "Click here to confirm it:\n" + link; let to = user.get("email"); diff --git a/src/ParseServer.js b/src/ParseServer.js index ab51f3d296..cf33b616ca 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -11,41 +11,40 @@ var batch = require('./batch'), Parse = require('parse/node').Parse, authDataManager = require('./authDataManager'); -//import passwordReset from './passwordReset'; -import cache from './cache'; -import Config from './Config'; -import parseServerPackage from '../package.json'; -import ParsePushAdapter from './Adapters/Push/ParsePushAdapter'; -import PromiseRouter from './PromiseRouter'; -import requiredParameter from './requiredParameter'; -import { AnalyticsRouter } from './Routers/AnalyticsRouter'; -import { ClassesRouter } from './Routers/ClassesRouter'; -import { FeaturesRouter } from './Routers/FeaturesRouter'; -import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; -import { FilesController } from './Controllers/FilesController'; -import { FilesRouter } from './Routers/FilesRouter'; -import { FunctionsRouter } from './Routers/FunctionsRouter'; -import { GlobalConfigRouter } from './Routers/GlobalConfigRouter'; -import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter'; -import { HooksController } from './Controllers/HooksController'; -import { HooksRouter } from './Routers/HooksRouter'; -import { IAPValidationRouter } from './Routers/IAPValidationRouter'; -import { InstallationsRouter } from './Routers/InstallationsRouter'; -import { loadAdapter } from './Adapters/AdapterLoader'; -import { LiveQueryController } from './Controllers/LiveQueryController'; -import { LoggerController } from './Controllers/LoggerController'; -import { LogsRouter } from './Routers/LogsRouter'; -import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer'; -import { PublicAPIRouter } from './Routers/PublicAPIRouter'; -import { PushController } from './Controllers/PushController'; -import { PushRouter } from './Routers/PushRouter'; -import { randomString } from './cryptoUtils'; -import { RolesRouter } from './Routers/RolesRouter'; -import { SchemasRouter } from './Routers/SchemasRouter'; -import { SessionsRouter } from './Routers/SessionsRouter'; -import { setFeature } from './features'; -import { UserController } from './Controllers/UserController'; -import { UsersRouter } from './Routers/UsersRouter'; +import cache from './cache'; +import Config from './Config'; +import parseServerPackage from '../package.json'; +import ParsePushAdapter from './Adapters/Push/ParsePushAdapter'; +import PromiseRouter from './PromiseRouter'; +import requiredParameter from './requiredParameter'; +import { AnalyticsRouter } from './Routers/AnalyticsRouter'; +import { ClassesRouter } from './Routers/ClassesRouter'; +import { FeaturesRouter } from './Routers/FeaturesRouter'; +import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; +import { FilesController } from './Controllers/FilesController'; +import { FilesRouter } from './Routers/FilesRouter'; +import { FunctionsRouter } from './Routers/FunctionsRouter'; +import { GlobalConfigRouter } from './Routers/GlobalConfigRouter'; +import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter'; +import { HooksController } from './Controllers/HooksController'; +import { HooksRouter } from './Routers/HooksRouter'; +import { IAPValidationRouter } from './Routers/IAPValidationRouter'; +import { InstallationsRouter } from './Routers/InstallationsRouter'; +import { loadAdapter } from './Adapters/AdapterLoader'; +import { LiveQueryController } from './Controllers/LiveQueryController'; +import { LoggerController } from './Controllers/LoggerController'; +import { LogsRouter } from './Routers/LogsRouter'; +import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer'; +import { PublicAPIRouter } from './Routers/PublicAPIRouter'; +import { PushController } from './Controllers/PushController'; +import { PushRouter } from './Routers/PushRouter'; +import { randomString } from './cryptoUtils'; +import { RolesRouter } from './Routers/RolesRouter'; +import { SchemasRouter } from './Routers/SchemasRouter'; +import { SessionsRouter } from './Routers/SessionsRouter'; +import { setFeature } from './features'; +import { UserController } from './Controllers/UserController'; +import { UsersRouter } from './Routers/UsersRouter'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); diff --git a/src/index.js b/src/index.js index 1427a5bfa6..791fea8db3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ import ParseServer from './ParseServer' -import { GCSAdapter } from 'parse-server-gcs-adapter'; -import { S3Adapter } from 'parse-server-s3-adapter'; -import { FileSystemAdapter } from 'parse-server-fs-adapter'; +import { FileSystemAdapter } from './Adapters/Files/FileSystemAdapter'; +import { GCSAdapter } from './Adapters/Files/GCSAdapter'; +import { S3Adapter } from './Adapters/Files/S3Adapter'; // Factory function let _ParseServer = function(options) { From f28b719b830c6f167a71462e7b9abe4a1ccc55ad Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 22 Mar 2016 22:22:53 -0700 Subject: [PATCH 2/8] Fix tests --- spec/index.spec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/index.spec.js b/spec/index.spec.js index bb902e05a8..cccbc8e120 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -56,6 +56,7 @@ describe('server', () => { fileKey: 'test', verifyUserEmails: true, emailAdapter: MockEmailAdapterWithOptions({ + fromAddress: 'parse@example.com', apiKey: 'k', domain: 'd', }), @@ -80,6 +81,7 @@ describe('server', () => { emailAdapter: { class: MockEmailAdapterWithOptions, options: { + fromAddress: 'parse@example.com', apiKey: 'k', domain: 'd', } @@ -105,6 +107,7 @@ describe('server', () => { emailAdapter: { module: './Email/SimpleMailgunAdapter', options: { + fromAddress: 'parse@example.com', apiKey: 'k', domain: 'd', } @@ -129,7 +132,7 @@ describe('server', () => { verifyUserEmails: true, emailAdapter: './Email/SimpleMailgunAdapter', publicServerURL: 'http://localhost:8378/1' - })).toThrow('SimpleMailgunAdapter requires an API Key and domain.'); + })).toThrow('SimpleMailgunAdapter requires an API Key, domain, and fromAddress.'); done(); }); @@ -153,7 +156,7 @@ describe('server', () => { } }, publicServerURL: 'http://localhost:8378/1' - })).toThrow('SimpleMailgunAdapter requires an API Key and domain.'); + })).toThrow('SimpleMailgunAdapter requires an API Key, domain, and fromAddress.'); done(); }); From 152c7e88b87e8bca51fa90a671ccdec0acd2c85f Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 23 Mar 2016 17:20:35 -0700 Subject: [PATCH 3/8] Move mailgun adapter to it's own repo --- package.json | 1 + src/Adapters/Email/SimpleMailgunAdapter.js | 32 ---------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 src/Adapters/Email/SimpleMailgunAdapter.js diff --git a/package.json b/package.json index edf21eb710..9bb62ab3be 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "parse-server-fs-adapter": "^1.0.0", "parse-server-gcs-adapter": "^1.0.0", "parse-server-s3-adapter": "^1.0.0", + "parse-server-simple-mailgun-adapter": "0.0.1", "redis": "^2.5.0-1", "request": "^2.65.0", "tv4": "^1.2.7", diff --git a/src/Adapters/Email/SimpleMailgunAdapter.js b/src/Adapters/Email/SimpleMailgunAdapter.js deleted file mode 100644 index f670429b63..0000000000 --- a/src/Adapters/Email/SimpleMailgunAdapter.js +++ /dev/null @@ -1,32 +0,0 @@ -import Mailgun from 'mailgun-js'; - -let SimpleMailgunAdapter = mailgunOptions => { - if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain || !mailgunOptions.fromAddress) { - throw 'SimpleMailgunAdapter requires an API Key, domain, and fromAddress.'; - } - let mailgun = Mailgun(mailgunOptions); - - let sendMail = ({to, subject, text}) => { - let data = { - from: mailgunOptions.fromAddress, - to: to, - subject: subject, - text: text, - } - - return new Promise((resolve, reject) => { - mailgun.messages().send(data, (err, body) => { - if (typeof err !== 'undefined') { - reject(err); - } - resolve(body); - }); - }); - } - - return Object.freeze({ - sendMail: sendMail - }); -} - -module.exports = SimpleMailgunAdapter From 259de3f104d163539b870b3ae0de892d14f5c7cd Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 23 Mar 2016 17:26:37 -0700 Subject: [PATCH 4/8] Fix readme to note move of mailgun adapter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29f0030641..2aaa1bbb89 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ var server = ParseServer({ appName: 'Parse App', // The email adapter emailAdapter: { - module: 'parse-server/lib/Adapters/Email/SimpleMailgunAdapter', + module: 'parse-server-simple-mailgun-adapter', options: { // The addres that your emails come from fromAddress: 'parse@example.com', From 958d77207aecd7da6176cec7ecd67965867e9075 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 23 Mar 2016 17:28:52 -0700 Subject: [PATCH 5/8] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2aaa1bbb89..4e3bd4a8ad 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ var server = ParseServer({ emailAdapter: { module: 'parse-server-simple-mailgun-adapter', options: { - // The addres that your emails come from + // The address that your emails come from fromAddress: 'parse@example.com', // Your domain from mailgun.com domain: 'example.com', From 3e45e04cf1a861a6455ea62cce0371905d696410 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 23 Mar 2016 17:30:48 -0700 Subject: [PATCH 6/8] Use version 1.0.0 of mailgun adapter --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9bb62ab3be..21d0990c77 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "parse-server-fs-adapter": "^1.0.0", "parse-server-gcs-adapter": "^1.0.0", "parse-server-s3-adapter": "^1.0.0", - "parse-server-simple-mailgun-adapter": "0.0.1", + "parse-server-simple-mailgun-adapter": "^1.0.0", "redis": "^2.5.0-1", "request": "^2.65.0", "tv4": "^1.2.7", From b4ee31322e39037ad361f62e8907f45fb6c1f1a6 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 23 Mar 2016 18:32:40 -0700 Subject: [PATCH 7/8] Fix tests --- spec/index.spec.js | 6 +++--- src/Adapters/AdapterLoader.js | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spec/index.spec.js b/spec/index.spec.js index cccbc8e120..5622730b99 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -105,7 +105,7 @@ describe('server', () => { fileKey: 'test', verifyUserEmails: true, emailAdapter: { - module: './Email/SimpleMailgunAdapter', + module: 'parse-server-simple-mailgun-adapter', options: { fromAddress: 'parse@example.com', apiKey: 'k', @@ -130,7 +130,7 @@ describe('server', () => { collectionPrefix: 'test_', fileKey: 'test', verifyUserEmails: true, - emailAdapter: './Email/SimpleMailgunAdapter', + emailAdapter: 'parse-server-simple-mailgun-adapter', publicServerURL: 'http://localhost:8378/1' })).toThrow('SimpleMailgunAdapter requires an API Key, domain, and fromAddress.'); done(); @@ -150,7 +150,7 @@ describe('server', () => { fileKey: 'test', verifyUserEmails: true, emailAdapter: { - module: './Email/SimpleMailgunAdapter', + module: 'parse-server-simple-mailgun-adapter', options: { domain: 'd', } diff --git a/src/Adapters/AdapterLoader.js b/src/Adapters/AdapterLoader.js index 654948e96c..5305b13531 100644 --- a/src/Adapters/AdapterLoader.js +++ b/src/Adapters/AdapterLoader.js @@ -10,8 +10,12 @@ export function loadAdapter(adapter, defaultAdapter, options) { try { return adapter(options); } catch(e) { - var Adapter = adapter; - return new Adapter(options); + if (e.name === 'TypeError') { + var Adapter = adapter; + return new Adapter(options); + } else { + throw e; + } } } else if (typeof adapter === "string") { adapter = require(adapter); @@ -19,7 +23,6 @@ export function loadAdapter(adapter, defaultAdapter, options) { if (adapter.default) { adapter = adapter.default; } - return loadAdapter(adapter, undefined, options); } else if (adapter.module) { return loadAdapter(adapter.module, undefined, adapter.options); From 27a3198d810c699a66c2575f12c059bd66f14ec9 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 24 Mar 2016 15:08:44 -0700 Subject: [PATCH 8/8] Fix imports --- src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 791fea8db3..d5e6d0c3e4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ -import ParseServer from './ParseServer' -import { FileSystemAdapter } from './Adapters/Files/FileSystemAdapter'; -import { GCSAdapter } from './Adapters/Files/GCSAdapter'; -import { S3Adapter } from './Adapters/Files/S3Adapter'; +import ParseServer from './ParseServer'; +import { GCSAdapter } from 'parse-server-gcs-adapter'; +import { S3Adapter } from 'parse-server-s3-adapter'; +import { FileSystemAdapter } from 'parse-server-fs-adapter'; // Factory function let _ParseServer = function(options) {