My project is working fine on localhost but when i use the heroku deployed version to send email, it gives an internal server error and does not specify any reason for it. Heroku logs are confusing as well and i am unable to decode the problem here, kindly help.
Github Repo Link , Heroku Link
The moment you will click on send after entering email, it will forever get stuck on it, this was not happening in local host.
I have already checked similar questions around this topic but to no avail, all of my env variables are perfectly placed and added in heroku as well.
Here is my a screenshots of errors:
Console Error
Error Description
Heroku Log
IndexJs (front-end)
Browser is showing error (internal server on line 150 int this file)
const dropZone = document.querySelector(".drop-zone");
const fileInput = document.querySelector("#fileInput");
const browseBtn = document.querySelector("#browseBtn");
// const bgProgress = document.querySelector(".bg-progress");
const progressPercent = document.querySelector("#progressPercent");
const progressContainer = document.querySelector(".progress-container");
const progressBar = document.querySelector(".progress-bar");
const status = document.querySelector(".status");
const sharingContainer = document.querySelector(".sharing-container");
const copyURLBtn = document.querySelector("#copyURLBtn");
const fileURL = document.querySelector("#fileURL");
const emailForm = document.querySelector("#emailForm");
const toast = document.querySelector(".toast");
const baseURL = "https://inshare-ak.herokuapp.com";
const uploadURL = `${baseURL}/api/files`;
const emailURL = `${baseURL}/api/files/send`;
const maxAllowedSize = 100 * 1024 * 1024; //100mb
browseBtn.addEventListener("click", () => {
fileInput.click();
});
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
// console.log("dropped", e.dataTransfer.files[0].name);
const files = e.dataTransfer.files;
if (files.length === 1) {
if (files[0].size < maxAllowedSize) {
fileInput.files = files;
uploadFile();
} else {
showToast("Max file size is 100MB");
}
} else if (files.length > 1) {
showToast("You can't upload multiple files");
}
dropZone.classList.remove("dragged");
});
dropZone.addEventListener("dragover", (e) => {
e.preventDefault();
dropZone.classList.add("dragged");
// console.log("dropping file");
});
dropZone.addEventListener("dragleave", (e) => {
dropZone.classList.remove("dragged");
console.log("drag ended");
});
// file input change and uploader
fileInput.addEventListener("change", () => {
if (fileInput.files[0].size > maxAllowedSize) {
showToast("Max file size is 100MB");
fileInput.value = ""; // reset the input
return;
}
uploadFile();
});
// sharing container listenrs
copyURLBtn.addEventListener("click", () => {
fileURL.select();
document.execCommand("copy");
showToast("Copied to clipboard");
});
fileURL.addEventListener("click", () => {
fileURL.select();
});
const uploadFile = () => {
console.log("file added uploading");
files = fileInput.files;
const formData = new FormData();
formData.append("myfile", files[0]);
//show the uploader
progressContainer.style.display = "block";
// upload file
const xhr = new XMLHttpRequest();
// listen for upload progress
xhr.upload.onprogress = function (event) {
// find the percentage of uploaded
let percent = Math.round((100 * event.loaded) / event.total);
progressPercent.innerText = percent;
const scaleX = `scaleX(${percent / 100})`;
// bgProgress.style.transform = scaleX;
progressBar.style.transform = scaleX;
};
// handle error
xhr.upload.onerror = function () {
showToast(`Error in upload: ${xhr.status}.`);
fileInput.value = ""; // reset the input
};
// listen for response which will give the link
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
onFileUploadSuccess(xhr.responseText);
}
};
xhr.open("POST", uploadURL);
xhr.send(formData);
};
const onFileUploadSuccess = (res) => {
fileInput.value = ""; // reset the input
status.innerText = "Uploaded";
// remove the disabled attribute from form btn & make text send
emailForm[2].removeAttribute("disabled");
emailForm[2].innerText = "Send";
progressContainer.style.display = "none"; // hide the box
const { file: url } = JSON.parse(res);
console.log(url);
sharingContainer.style.display = "block";
fileURL.value = url;
};
emailForm.addEventListener("submit", (e) => {
e.preventDefault(); // stop submission
// disable the button
emailForm[2].setAttribute("disabled", "true");
emailForm[2].innerText = "Sending";
const url = fileURL.value;
const formData = {
uuid: url.split("/").splice(-1, 1)[0],
emailTo: emailForm.elements["to-email"].value,
emailFrom: emailForm.elements["from-email"].value,
};
console.log(formData);
fetch(emailURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
.then((res) => res.json())
.then((data) => {
if (data.success) {
showToast("Email Sent");
sharingContainer.style.display = "none"; // hide the box
}
}).catch((err) => console.log({ err: "Post req returned this error" }));
});
let toastTimer;
// the toast function
const showToast = (msg) => {
clearTimeout(toastTimer);
toast.innerText = msg;
toast.classList.add("show");
toastTimer = setTimeout(() => {
toast.classList.remove("show");
}, 2000);
};
ServerJs code
require('dotenv').config();
const express = require('express');
const app = express();
// const apiRoutes = require('./routes/files')
const path = require('path');
const PORT = process.env.PORT || 3000;
const cors = require('cors')
// Cors
const corsOptions = {
origin: process.env.ALLOWED_CLIENTS
}
app.use(cors(corsOptions));
app.use(express.static('public'));
const connectDB = require('./config/db')
connectDB();
app.use(express.json());
app.get('/', (req, res) => {
res.render('index', { title: "file sharing made easy" });
});
app.get('', (req, res) => {
res.redirect('/');
});
//Template Engine
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
// Routes
app.use('/api/files', require('./routes/files'));
app.use('/files', require('./routes/show'));
app.use('/files/download', require('./routes/download'))
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
/api/files/send Route
const router = require('express').Router();
const multer = require('multer');
const path = require('path');
const File = require('../models/file');
const { v4: uuidv4 } = require('uuid');
let storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const uniqueName =
`${Date.now()}-${Math.round(Math.random() *
1E9)}${path.extname(file.originalname)}`;
cb(null, uniqueName)
},
});
let upload = multer({ storage, limits: { fileSize: 1000000 * 100 }, }).single('myfile'); //100mb
router.post('/', (req, res) => {
upload(req, res, async (err) => {
if (err) {
return res.status(500).send({ error: err.message });
}
const file = new File({
filename: req.file.filename,
uuid: uuidv4(),
path: req.file.path,
size: req.file.size
});
const response = await file.save();
res.json({ file: `${process.env.APP_BASE_URL}/files/${response.uuid}` });
});
});
router.post('/send', async (req, res) => {
const { uuid, emailTo, emailFrom } = req.body;
if (!uuid || !emailTo) {
return res.status(422).send({ error: 'All fields are required except expiry.' });
}
// Get data from db
try {
const file = await File.findOne({ uuid: uuid });
if (file.sender) {
return res.status(422).send({ error: 'Email already sent once.' });
}
file.sender = emailFrom;
file.receiver = emailTo;
const response = await file.save();
// send mail
const sendMail = require('../services/mailService');
sendMail({
from: emailFrom,
to: emailTo,
subject: 'inShare file sharing',
text: `${emailFrom} shared a file with you.`,
html: require('../services/emailTemplate')({
emailFrom: emailFrom,
downloadLink: `${process.env.APP_BASE_URL}/files/${file.uuid}?source=email`,
size: parseInt(file.size / 1000) + ' KB',
expires: '24 hours'
})
}).then(() => {
return res.json({ success: true });
}).catch(err => {
return res.status(500).json({ error: 'Error in email sending.' });
});
} catch (err) {
return res.status(500).send({ error: 'Something went wrong.' });
}
});
module.exports = router;
EmailServiceJs
const nodemailer = require('nodemailer');
async function sendMail({ from, to, subject, text, html }) {
let transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: false,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASSWORD
}
})
let info = await transporter.sendMail({
from: 'inshare <atikmansuri619@gmail.com>',
to, /* == to: to in js if key and value are same then we can just write it once #destructuring */
subject: subject,
text: text,
html: html,
});
console.log(info)
}
module.exports = sendMail;