diff --git a/TODO.md b/TODO.md index 32f668e..b909f76 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,5 @@ ## TODO -* mailer +* no hardcoded URLS ## Maybes * gallery page - featured images in same style as on services page with the @@ -8,7 +8,7 @@ ## Fixes * refreshing page on gallery and then quickly usingh history buttons breaks it -* broken layout at exactly 40em (640px) breakpoint +* broken layout at exactly 40em (640px) breakpoint (just on galleries?) * galleries - click on thumb, click on different gallery, click on original gallery and the thumb list doesn't scroll to correct position to show thumb 0, it is still showing the thumb that was clicked on first diff --git a/api/contact.js b/api/contact.js new file mode 100644 index 0000000..8b89e04 --- /dev/null +++ b/api/contact.js @@ -0,0 +1,63 @@ +import express from 'express' +const nodemailer = require('nodemailer') +const validator = require('validator') +const xssFilters = require('xss-filters') + +const app = express() +app.use(express.json()) + +app.post('/', function (req, res) { + const attributes = ['name', 'email', 'msg'] + const sanitizedAttributes = attributes.map(n => validateAndSanitize(n, req.body[n])) + const someInvalid = sanitizedAttributes.some(r => !r) + + if (someInvalid) { + return res.status(400).json({ 'error': 'bad request'}) + } + + sendMail(...sanitizedAttributes) + return res.status(200).json({ 'message': 'success'}) +}) + +export default { + path: '/api/contact', + handler: app, +} + +function validateAndSanitize (key, value) { + const rejectFunctions = { + name: v => v.length < 4, + email: v => !validator.isEmail(v), + msg: v => v.length < 1, + } + + if (value === undefined || value.length < 1) { return false } + + // if object has key and function returns false, return sanitised input. + // Else, return false + return rejectFunctions.hasOwnProperty(key) && !rejectFunctions[key](value) && xssFilters.inHTMLData(value) +} + +function sendMail (name, email, msg) { + const transporter = nodemailer.createTransport({ + sendmail: true, + newline: 'unix', + path: '/usr/sbin/sendmail' + }) + + const text = +`Message from ${name}: + +${msg}` + + const mailJson = { + from: 'server@gabbaell.co.uk', + replyTo: email, + to: 'marcleopold.isnet@gabbaell.co.uk', + subject: 'Contact form message concerning Marc Leopold', + text: text, + } + + transporter.sendMail(mailJson) +} + diff --git a/assets/scss/_buefy.scss b/assets/scss/_buefy.scss index 0feccad..7c3de38 100644 --- a/assets/scss/_buefy.scss +++ b/assets/scss/_buefy.scss @@ -11,6 +11,7 @@ $success: $color__accent-success-300; @import "~buefy/src/scss/buefy"; .label { + position: relative; color: $color__neutral-700; @include font-title(600); } diff --git a/nuxt.config.js b/nuxt.config.js index 7d2f5ec..c680ce6 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -7,6 +7,9 @@ module.exports = { port: 3003, host: '0.0.0.0' }, + serverMiddleware: [ + '~/api/contact', + ], /* ** Headers of the page */ diff --git a/package.json b/package.json index befbfd3..03f33c8 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,14 @@ "@nuxtjs/axios": "^5.3.6", "@nuxtjs/proxy": "^1.3.1", "cross-env": "^5.2.0", + "express": "^4.16.4", + "nodemailer": "^5.1.1", "nuxt": "^2.0.0", "nuxt-buefy": "^0.2.1", "nuxt-sass-resources-loader": "^2.0.5", - "vuelidate": "^0.7.4" + "validator": "^10.11.0", + "vuelidate": "^0.7.4", + "xss-filters": "^1.2.7" }, "devDependencies": { "babel-eslint": "^8.2.1", diff --git a/pages/contact.vue b/pages/contact.vue index 9191578..8f0337a 100644 --- a/pages/contact.vue +++ b/pages/contact.vue @@ -20,7 +20,7 @@ > + placeholder="Your email address ..."> @@ -148,17 +148,40 @@ export default { message: 'Please correct errors before submitting.', type: 'is-danger' }) + } else { + this.$toast.open('Submitting your message ...') + this.submitForm() + } + }, + + async submitForm() { + try { + await this.$axios({ + method: 'post', + baseURL: 'http://192.168.0.5:3003', + url: '/api/contact', + proxy: false, + debug: true, + data: { + name: this.form.name, + email: this.form.email, + msg: this.form.message + }, + }) + } catch (e) { + this.$toast.open({ + message: 'sorry, there was a problem submitting your message.', + type: 'is-danger' + }) + console.error(e) return } - this.$toast.open('Submitting your message ...') - window.setTimeout(() => { - this.$toast.open({ - message: 'Thank you, your message has been succesfully sent.', - type: 'is-success' - }) - this.form.message = '' - this.form.subject = '' - }, 1000) + this.$toast.open({ + message: 'Thank you, your message has been sent.', + type: 'is-success' + }) + this.form.message = '' + this.form.subject = '' }, }, diff --git a/yarn.lock b/yarn.lock index d9798cc..8007df0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3221,7 +3221,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -express@^4.16.3: +express@^4.16.3, express@^4.16.4: version "4.16.4" resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== @@ -5178,6 +5178,11 @@ node-sass@^4.11.0: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" +nodemailer@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-5.1.1.tgz#0c48d1ecab02e86d9ff6c620ee75ed944b763505" + integrity sha512-hKGCoeNdFL2W7S76J/Oucbw0/qRlfG815tENdhzcqTpSjKgAN91mFOqU2lQUflRRxFM7iZvCyaFcAR9noc/CqQ== + nodemon@^1.11.0: version "1.18.9" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.9.tgz#90b467efd3b3c81b9453380aeb2a2cba535d0ead" @@ -7972,6 +7977,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validator@^10.11.0: + version "10.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228" + integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -8304,6 +8314,11 @@ xdg-basedir@^3.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= +xss-filters@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/xss-filters/-/xss-filters-1.2.7.tgz#59fa1de201f36f2f3470dcac5f58ccc2830b0a9a" + integrity sha1-Wfod4gHzby80cNysX1jMwoMLCpo= + xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"