parse-community / parse-server-api-mail-adapter

API Mail Adapter for Parse Server
MIT License
27 stars 17 forks source link

Error: ApiMailAdapter: template content path is invalid #53

Closed tiavina-mika closed 2 years ago

tiavina-mika commented 2 years ago

New Issue Checklist

Issue Description

I'm getting this error: Error: ApiMailAdapter: template content path is invalid with my config. my filePath is E:\tiavina-mika\portfolio-v3\email\templates\projects\projectsSubject.txt

Steps to reproduce

here is my files:

mailgunConfig.js

const path = require('path');

const formData = require('form-data');
const Mailgun = require('mailgun.js');
const { ApiPayloadConverter } = require('parse-server-api-mail-adapter');

const filePath = (file, folder) => path.join(__dirname, 'templates/' + (folder ? folder + '/' : ''), file);

const getEmailAdapter = () => {
    // Configure mail client
    const mailgun = new Mailgun(formData);
    const mailgunClient = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY });
    const mailgunDomain = process.env.MAILGUN_DOMAIN;
    console.log('filePath(): ', filePath('projectsSubject.txt', 'projects'));
    const emailAdapter = {
        module: 'parse-server-api-mail-adapter',
        options: {
            // The email address from which emails are sent.
            sender: 'tikskun@gmail.com',
            // The email templates.
            templates: {
                // The template used by Parse Server to send an email for password
                // reset; this is a reserved template name.
                // passwordResetEmail: {
                //     subjectPath: './files/password_reset_email_subject.txt',
                //     textPath: './files/password_reset_email.txt',
                //     htmlPath: './files/password_reset_email.html'
                // },
                // // The template used by Parse Server to send an email for email
                // // address verification; this is a reserved template name.
                // verificationEmail: {
                //     subjectPath: './files/verification_email_subject.txt',
                //     textPath: './files/verification_email.txt',
                //     htmlPath: './files/verification_email.html'
                // },
                // A custom email template that can be used when sending emails
                // from Cloud Code; the template name can be chosen freely; it
                // is possible to add various custom templates.
                projectEmail: {
                    // subjectPath: './files/custom_email_subject.txt',
                    // textPath: './files/custom_email.txt',
                    // subjectPath: './emailTemplates/projects/projectsSubject.txt',
                    subjectPath: filePath('projectsSubject.txt', 'projects'),
                    htmlPath: filePath('projects.html', 'projects'),
                    // Placeholders are filled into the template file contents.
                    // For example, the placeholder `{{appName}}` in the email
                    // will be replaced the value defined here.
                    placeholders: {
                        appName: 'ExampleApp',
                    },
                    // Extras to add to the email payload that is accessible in the
                    // `apiCallback`.
                    // extra: {
                    //     replyTo: 'no-reply@example.com'
                    // },
                    // A callback that makes the Parse User accessible and allows
                    // to return user-customized placeholders that will override
                    // the default template placeholders. It also makes the user
                    // locale accessible, if it was returned by the `localeCallback`,
                    // and the current placeholders that will be augmented.
                    // placeholderCallback: async ({ user, locale, placeholders }) => {
                    //     return {
                    //         phone: user.get('phone');
                    //     };
                    // },
                    // A callback that makes the Parse User accessible and allows
                    // to return the locale of the user for template localization.
                    // localeCallback: async (user) => {
                    //     return user.get('locale');
                    // }
                },
            },
            // The asynchronous callback that contains the composed email payload to
            // be passed on to an 3rd party API and optional meta data. The payload
            // may need to be converted specifically for the API; conversion for
            // common APIs is conveniently available in the `ApiPayloadConverter`.
            // Below is an example for the Mailgun client.
            apiCallback: async ({ payload }) => {
                const mailgunPayload = ApiPayloadConverter.mailgun(payload);
                await mailgunClient.messages.create(mailgunDomain, mailgunPayload);
            },
        },
    };

    return emailAdapter;
};

exports.getEmailAdapter = getEmailAdapter;

server.js (Next.js custom server + express)

const express = require('express');
const ParseServer = require('parse-server').ParseServer;
const path = require('path');
const next = require('next');
const cors = require('cors');
const dotenv = require('dotenv');
const Parse = require('parse/node');
const { getEmailAdapter } = require('./mailgunConfig');

const SERVER_PORT = process.env.PORT || 3001;
const APP_ID = process.env.APP_ID || 'myApp';
global.APP_ID = APP_ID;
const MASTER_KEY = process.env.MASTER_KEY || 'xxxx';
const IS_DEVELOPMENT = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev: IS_DEVELOPMENT });
const handle = nextApp.getRequestHandler();

let envFileName;
let serverUrl;
if (IS_DEVELOPMENT) {
  envFileName = '.env.local';
  serverUrl = 'http://localhost:' + SERVER_PORT;
} else {
  envFileName = '.env.prod';
  serverUrl = 'https://my-app.herokuapp.com';
}

global.USE_MASTER_KEY = { useMasterKey: true };
global.IS_DEVELOPMENT = IS_DEVELOPMENT;

const result = dotenv.config({ path: path.join(__dirname, envFileName) });

if (result.error) {
  throw result.error;
}

const parseServerAPI = new ParseServer({
  databaseURI: process.env.DB_URL,
  cloud: path.resolve(__dirname, './cloud/main.js'),
  appId: APP_ID,
  masterKey: MASTER_KEY,
  serverURL: serverUrl + '/parse',

  // verifyUserEmails: true,
  // emailVerifyTokenValidityDuration: 2 * 60 * 60,
  // preventLoginWithUnverifiedEmail: false,
  emailAdapter: getEmailAdapter(),
});

global.USE_MASTER_KEY = { useMasterKey: true };
global.LOCAL = true;

nextApp
  .prepare()
  .then(() => {
    Parse.initialize('myApp');
    Parse.serverURL = `${serverUrl}/api/parse`;
    Parse.masterKey = MASTER_KEY;

    const app = express();

    app.use(cors());
    // Add headers
    app.use((req, res, next) => {
      // Set to true if you need the website to include cookies in the requests sent
      // to the API (e.g. in case you use sessions)
      res.setHeader('Access-Control-Allow-Credentials', true);

      // Pass to next layer of middleware
      next();
    });

    app.use('/parse', parseServerAPI);

    app.all('*', (req, res) => {
      return handle(req, res);
    });

    app.listen(SERVER_PORT, (err) => {
      if (err) throw err;
      console.log(
        `Notre serveur tourne en mode ${process.env.NODE_ENV ||
          'development'} sur ${serverUrl}`
      );
    });
  })
  .catch((error) => {
    console.error(error.stack);
    process.exit(1);
  });

folder structures

image

Actual Outcome

` E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:601 throw Errors.Error.templateContentPathInvalid; ^

Error: ApiMailAdapter: template content path is invalid. at Function.get (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\Errors.js:62:37) at ApiMailAdapter._validateTemplate (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:601:22) at new ApiMailAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:83:13) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:35:16) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:48:12) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:50:12) at getUserController (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Controllers\index.js:169:65) at Object.getControllers (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Controllers\index.js:81:26) at new ParseServer (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\ParseServer.js:110:40) at new _ParseServer (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\index.js:92:18) [nodemon] app crashed - waiting for file changes before starting... `

Expected Outcome

[nodemon] startingnode server.js info - Loaded env from E:\tiavina-mika\portfolio-v3\.env.local info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5 info - Using external babel configuration from E:\tiavina-mika\portfolio-v3\.babelrc (node:13856) DeprecationWarning: Listening to events on the Db class has been deprecated and will be removed in the next major version. (Usenode --trace-deprecation ...to show where the warning was created) event - compiled successfully Notre serveur tourne en mode development sur http://localhost:3001 event - build page: /api/parse/[...id] wait - compiling... event - compiled successfully

Environment

Node: 14.15.4 Npm: 7.20.1 "next": "11.1.2"

Logs

E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:601 throw Errors.Error.templateContentPathInvalid; ^

Error: ApiMailAdapter: template content path is invalid. at Function.get (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\Errors.js:62:37) at ApiMailAdapter._validateTemplate (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:601:22) at new ApiMailAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server-api-mail-adapter\lib\ApiMailAdapter.js:83:13) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:35:16) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:48:12) at loadAdapter (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Adapters\AdapterLoader.js:50:12) at getUserController (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Controllers\index.js:169:65) at Object.getControllers (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\Controllers\index.js:81:26) at new ParseServer (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\ParseServer.js:110:40) at new _ParseServer (E:\tiavina-mika\portfolio-v3\node_modules\parse-server\lib\index.js:92:18) [nodemon] app crashed - waiting for file changes before starting...

mtrezza commented 2 years ago

The file paths only seem to include the filename, but no path. Check whether that is correct.

tiavina-mika commented 2 years ago

here is the log of the path: E:\tiavina-mika\portfolio-v3\templates\projects\projectsSubject.txt it seems correct

mtrezza commented 2 years ago

Yes, I just noticed you are using a custom function to generate the path.

The issue is that textPath is not optional, only htmlPath is optional:

https://github.com/mtrezza/parse-server-api-mail-adapter/blob/4aca3a6dc06052b164b33bbe42303d32ee42f448/src/ApiMailAdapter.js#L360-L362

The textPath should point to a file that is used in case a mail browser is not able to display HTML.

tiavina-mika commented 2 years ago

ah ok. thanks. But the doc is not clear, how to send an email manually? for exemple, I have a Project class, I want to send email containing the project description every time I added a new "project", in the Parse.Cloud.beforeSave for exemple

mtrezza commented 2 years ago

See the example. For general support I suggest to post in the Parse Community Forum.

I'm closing this as it doesn't seem to be an issue with the mail adapter.