2

I have a React webapp with a Node.js server, the .htaccess file of my website used to redirect the webpages requests to index.html this however meant that the emails being sent from the forms on the site were not being delivered since the requests had to be redirected to the Node Environment. This was fixed by redirecting the requests to the Node Environment instead of the index.html inside my .htaccess file, however, this means that my server-side routing now broke and I am now getting the Cannot GET /end-point error whenever I type in the URL with an end-point in the browser window or if I refresh the page.

old .htaccess:

RewriteBase /
RewriteRule ^index\.html$ - [L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

current .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Is there a fix without either breaking my server-side routing or email delivering?

Edit:

server.js:

const express = require("express");
const app = express();
require("dotenv").config({path: "../.env"});
const bodyParser = require("body-parser");
const cors = require("cors");
const nodemailer = require("nodemailer");
var sgTransport = require('nodemailer-sendgrid-transport');
const multer = require("multer");

app.use(express.urlencoded({extended: true}));
app.use(express.json());

app.use(cors());

const transport = nodemailer.createTransport(sgTransport({
  auth: {
    api_key: process.env.SG_API_KEY
  }
}));

app.post("/send_schools_form", cors(), async (req, res) => {
  let {
    school,
    address,
    name,
    phone,
    email,
    substitutes
  } = req.body;

  var substitutesList = substitutes.reduce(function(a, b) {
  return a + '</br><ul><li>Startdatum: ' + b.date + '</li><li>Tid: ' + b.time + '</li>    <li>Info: ' + b.info + '</li></ul>';
}, '');

  await transport.sendMail({
    from: process.env.USER_SENDER,
    to: process.env.USER_RECEIVER,
    subject: `Vikarie begäran ${school}`,
    html: `
    <ul>
      <li>Skola: ${school}</li>
      <li>Adress: ${address}</li>
      <li>Kontaktperson: ${name}</li>
      <li>Telefon: ${phone}</li>
      <li>E-post: ${email}</li>
    </ul>
    <ul>${substitutesList}</ul>
    `
  }, (err) => {
    if (err) {
      console.log(err);
      res.status(400).send('Error');
    } else {
      res.status(200).send('Success');
    }
  })
});

const upload = multer({
  storage: multer.memoryStorage()
});

let middleware = [
  cors(),
  upload.fields([
    {name: "cvFile", maxCount: 1},
    {name: "otherFile", maxCount: 1}
  ])
];

app.post("/send_substitutes_form", middleware, async (req, res) => {
  let {
    firstName,
    lastName,
    email,
    phone,
    address,
    city,
    postalCode,
    availability
  } = req.body;

  await transport.sendMail({
    from: process.env.USER_SENDER,
    to: process.env.USER_RECEIVER,
    subject: `Arbetsansökan ${firstName} ${lastName}`,
    html: `
    <ul>
      <li>Namn: ${firstName} ${lastName}</li>
      <li>E-post: ${email}</li>
      <li>Telefon: ${phone}</li>
      <li>Adress: ${address}</li>
      <li>Ort: ${city}, ${postalCode}</li>
      <li>Tillgänglighet: ${availability} dagar per vecka</li>
    </ul>
    `,
    attachments: [{
      filename: req.files["cvFile"][0].originalname,
      content: req.files["cvFile"][0].buffer
    },
    {
      filename: req.files["otherFile"][0].originalname,
      content: req.files["otherFile"][0].buffer
    }]
  }, (err) => {
    if (err) {
      console.log(err);
      res.status(400).send('Error');
    } else {
      res.status(200).send('Success');
    }
  })
});

const PORT = process.env.PORT || 4000;

app.listen(PORT, () => {
  console.log("Server is listening on port " + PORT);
});
random1234
  • 777
  • 3
  • 17
  • 41

2 Answers2

1

You are doing it wrong, you should not redirect with 301.

I'm assuming you are sending an email by post an http request to your backend, so you need to set all your backend with specific path like /api. Then you could use this answers to solve your problem:

RewriteEngine on

RewriteCond %{REQUEST_URI} ^/api/
RewriteRule (.*) /api/public/index.php [L]

RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

Check this and this

yeya
  • 1,968
  • 1
  • 21
  • 31
  • Hey, can you read my edited question, I added my `server.js` file. I am sending the emails using the SendGrid API. – random1234 Jan 06 '23 at 09:07
  • It doesn't matter at all. your question is about apache, not nodejs, and not sendgrid. – yeya Jan 06 '23 at 09:10
  • BTW if your only need to send an email, you can maybe use sendgrid forms and ditch the server https://sendgrid.com/solutions/email-marketing/signup-forms/ – yeya Jan 06 '23 at 09:11
  • So the path of `/api/public/index.php` should be the path to my `server.js`? – random1234 Jan 06 '23 at 09:17
  • The path should be what ever you like. Apache cannot execute nodejs by itself anyway – yeya Jan 06 '23 at 09:24
  • Forgive me for my lack of knowledge, but I know basically nothing about `apache` or `.htaccess`, the `server.js` you can see above IS my backend, that is from where I send the emails using the SendGrid API. In the React.js frontend I have made forms that send `fetch()` and `axios.post()` requests to the node `server.js`. – random1234 Jan 06 '23 at 09:30
  • "You are doing it wrong, you should not redirect with 301." - The "301 redirect" in the OPs question is simply a canonical HTTP to HTTPS redirect (which is correct), and doesn't really have anything to do with this question. – MrWhite Jan 06 '23 at 11:38
  • If you only care about nodejs, why are we talking about .htaccess at all? Where do you host your nodejs site? .htaccess have nothing to do with nodejs, unless you are using it as reverse proxy – yeya Jan 07 '23 at 18:20
  • @yeya I never claimed to only care about Node.js, what I wrote was that I know close to nothing about Apache or the `.htaccess` file so I can't really understand your directions. What I tried to explain was that my `server.js` file was my only "backend" since you advised me to place my "backend" in a specific path. And to answer your last question, the site is hosted on A2hosting. – random1234 Jan 11 '23 at 16:51
  • Ask a2hosting support. Maybe that will help you https://www.a2hosting.com/kb/installable-applications/manual-installations/installing-node-js-on-managed-hosting-accounts – yeya Jan 11 '23 at 19:12
0

The issue was solved by adding this "catch-all" route near the bottom of my server.js file, after the other routes that post the requests to send the emails.

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '/path/to/index.html'));
});

This way, it will only be executed if none of the previous routes matches the request.

Also don't forget the import statement for path at the top:

const path = require("path");
random1234
  • 777
  • 3
  • 17
  • 41