Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

websmtp integration #188

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,11 @@ The secret is a key for encrypting JWTs. It can be modified like so:
SECRET = "any string"
'''

<b>SMTP:</b><br>
Configurations for the SMTP mailer. This is disabled in development environments, but can be modified with the following options:
1. `SMTP_HOST`: host address for the SMTP server.
2. `SMTP_PORT`: port for the host (see above).
3. `SMTP_USER`: the server username.
4. `SMTP_PASS`: the server password.
<b>SMTP_HOST:</b><br>
For SMTP, the website integrates with [OpenWebServices](https://github.com/ufosc/OpenWebServices) websmtp. The variable is configured as follows:
```
SMTP_HOST = "http://localhost:8080/mail/send"
``

<b>ADMIN_EMAIL</b><br>
The email address to send contact form confirmation emails from. Can be set to anything so long as its a valid email address. Modified as follows:
Expand All @@ -123,7 +122,7 @@ ADMIN_EMAIL = "[email protected]"
## Maintainers
Maintained by the UF Open Source Club, can be contacted via [Discord](https://discord.gg/j9g5dqSVD8)

Current Maintainers:
Current Maintainers:
- Michail Zeipekki @zeim839
- Daniel Wildsmith @danielwildsmith

Expand Down
73 changes: 57 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"standard": "^17.0.0"
},
"dependencies": {
"axios": "^1.6.5",
"body-passer": "^1.0.0",
"chai-http": "^4.3.0",
"compression": "^1.7.4",
Expand All @@ -53,7 +54,6 @@
"mongoose": "^6.12.0",
"multer": "^1.4.5-lts.1",
"node-fetch": "^3.3.2",
"nodemailer": "^6.9.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
Expand Down
129 changes: 65 additions & 64 deletions routes/contact.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
const express = require('express')
const router = express.Router()
const nodemailer = require('nodemailer')
const config = require('../utils/config')
const FormData = require('form-data')
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args))

const transport = (config.smtp == null) ? null : nodemailer.createTransport(config.smtp)
const axios = require('axios').default

// Regex to validate an email address
const validateAddress = (req, res, next) => {
Expand Down Expand Up @@ -57,7 +54,7 @@ const validateContactForm = async (req, res, next) => {
}

if (typeof req.body.Token !== 'string') {
return res.status(400).send({ error: 'Expected captcha token.' })
return res.status(400).send({ error: 'Bad captcha token. Please refresh the page and try again.' })
}

// Sanitise HTML
Expand All @@ -81,77 +78,81 @@ const validateTurnstile = async (req, res, next) => {
formData.append('response', token)
formData.append('remoteip', ip)

const ftc = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
body: formData
}
)
try {
const response = await axios.post(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
formData)

// Compare to true in case response is undefined.
const valid = await ftc.json()
if (!valid.success) {
if (response.status === 200 && typeof response.data !== 'undefined' && response.data.success) {
next()
} else {
return res.status(401).send({ error: 'Bad CAPTCHA Response' })
}
} catch (error) {
return res.status(401).send({ error: 'Bad CAPTCHA Response' })
}

next()
}

// Contact form message submission route
router.post('/', validateContactForm, validateTurnstile,
validateAddress, async (req, res) => {
if (transport == null) {
return res.status(400).send({ error: 'SMTP has not been configured' })
}

const message = {
FirstName: req.body.FirstName,
LastName: req.body.LastName,
Email: req.body.Email,
Message: req.body.Message
firstName: req.body.FirstName,
lastName: req.body.LastName,
email: req.body.Email,
message: req.body.Message
}

// Send confirmation receipt to sender
let info = await transport.sendMail({
subject: 'Your message was received',
text: `Dear ${message.FirstName} ${message.LastName},

Thank you for contacting the UF Open Source Club, this is an automated email confirming receipt of your message.
A club representative will be in touch soon.

Kindly,
UF OSC`,
from: '[email protected]',
to: req.body.Email
})

if (info.rejected.length !== 0) {
return req.status(400).send({ error: 'Message was rejected by server. Please try again later.' })
try {
// Send message receipt to sender.
const toSender = await axios.post(config.smtp_host, {
from: '[email protected]',
to: [message.email],
subject: '[UF OSC] Your message has been received',
body: `Thank you for contacting the UF OSC team, your message has been received.<br />
A representative will reach out within 24-48 hours.<br />
<br />
Sincerely,<br />
UF Open Source Club Team`
})

if (toSender.status !== 200) {
return res.status(400).send({
error: 'Your message could not be sent. Please try again later.'
})
}

// Send copy of message to admin.
const toAdmin = await axios.post(config.smtp_host, {
from: '[email protected]',
to: [config.admin_email],
subject: '[UF OSC] New Message from Website',
body: `You've received a new message via the OSC website contact form.<br />
<br />
First Name: ${message.firstName}<br />
Last Name: ${message.lastName}<br />
Email Address: ${message.email}<br />
<br />
### BEGIN MESSAGE ###<br />
${message.message}
<br />### END MESSAGE ###<br />
<br />
Please respond by composing a new email to ${message.email}. Replies to this message will not be delivered.
`
})

if (toAdmin.status !== 200) {
return res.status(400).send({
error: 'Your message could not be sent. Please try again later.'
})
}

return res.status(200).send('Success')
} catch (error) {
return res.status(400).send({
error: 'Your message could not be sent. Please try again later.'
})
}

// Send message confirmation receipt to UF OSC admin
info = await transport.sendMail({
subject: 'New Message from Website',
text: `${message.FirstName} ${message.LastName} sent a message via your website's contact form.
A copy of the message is attached below:

--- BEGIN MESSAGE ---
${message.Message}
--- END MESSAGE ---

You can reach this user via their email address: ${message.Email}
or by replying to this email.
`,
from: '[email protected]',
to: config.admin_email
})

if (info.rejected.length !== 0) {
return req.status(400).send({ error: 'Message was rejected by server. Please try again later,' })
}

return res.status(200).send('Success')
})

module.exports = router
22 changes: 3 additions & 19 deletions utils/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const config = {
},
cache_interval: process.env.CACHE_INTERVAL * 60 * 1000 || 10 * 1000,
secret: 'jLJhDQMYtXjCwQmu',
smtp: null,
smtp_host: process.env.SMTP_HOST || 'http://localhost:30000/mail/send',
admin_email: process.env.ADMIN_EMAIL,
captcha_secret: '1x0000000000000000000000000000000AA'
},
Expand All @@ -36,15 +36,7 @@ const config = {
},
cache_interval: process.env.CACHE_INTERVAL * 60 * 1000 || 15 * 60 * 1000,
secret: process.env.SECRET,
smtp: {
host: process.env.SMTP_HOST || 'smtp-relay.sendinblue.com',
port: process.env.SMTP_PORT || 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
},
smtp_host: process.env.SMTP_HOST || 'http://localhost:30000/mail/send',
admin_email: process.env.ADMIN_EMAIL,
captcha_secret: process.env.CAPTCHA_SECRET
},
Expand All @@ -58,15 +50,7 @@ const config = {
},
cache_interval: process.env.CACHE_INTERVAL * 60 * 1000 || 15 * 60 * 1000,
secret: process.env.SECRET,
smtp: {
host: process.env.SMPT_HOST || 'smtp-relay.sendinblue.com',
port: process.env.SMPT_PORT || 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMPT_PASS
}
},
smtp_host: process.env.SMTP_HOST,
admin_email: process.env.ADMIN_EMAIL,
captcha_secret: process.env.CAPTCHA_SECRET
}
Expand Down
Loading