diff --git a/app.js b/app.js index 59af17e..76b5f68 100644 --- a/app.js +++ b/app.js @@ -5,6 +5,7 @@ let session = require("express-session"); let logger = require("morgan"); let { I18n } = require("i18n"); let Recaptcha = require("express-recaptcha").RecaptchaV3; +let SMTPClient = require("emailjs").SMTPClient; let config = process.env.NODE_ENV === "test" ? {} : require("./config/config.json"); let indexRouter = require("./routes/index"); @@ -16,6 +17,7 @@ let ordersRouter = require("./routes/orders"); let sandwichesRouter = require("./routes/sandwiches"); let profileRouter = require("./routes/profile"); let adminRouter = require("./routes/admin"); +let contactRouter = require("./routes/contact"); let app = express(); let sess = { @@ -29,7 +31,9 @@ let i18n = new I18n({ directory: __dirname + "/locales", objectNotation: true }); -let recaptcha = process.env.NODE_ENV === "test" ? null : new Recaptcha(config.siteKey, config.secretKey, {callback: "cb"}); +let recaptcha = process.env.NODE_ENV === "test" ? null : new Recaptcha(config.reCaptcha.siteKey, + config.reCaptcha.secretKey, {callback: "cb"}); +let mailClient = new SMTPClient(process.env.NODE_ENV === "test" ? {} : config.email.server); if (app.get("env") === "production") { app.set("trust proxy", 1); @@ -39,7 +43,9 @@ if (app.get("env") === "production") { // view engine setup app.set("views", path.join(__dirname, "views")); app.set("view engine", "pug"); +app.set("config", config); app.set("recaptcha", recaptcha); +app.set("mailClient", mailClient); app.use(logger("dev")); app.use(express.json()); @@ -65,6 +71,7 @@ app.use("/orders", ordersRouter); app.use("/sandwiches", sandwichesRouter); app.use("/profile", profileRouter); app.use("/admin", adminRouter); +app.use("/contact", contactRouter); // catch 404 and forward to error handler app.use((req, res) => { diff --git a/config/config_exemple.json b/config/config_exemple.json index f9a5ad2..bcb8693 100644 --- a/config/config_exemple.json +++ b/config/config_exemple.json @@ -7,6 +7,18 @@ "host": "127.0.0.1", "dialect": "mysql" }, - "siteKey": "yjt,kugcjvhkyhgchkuyjgugjgcvkuh", - "secretKey": "yjt,vhjtfykjvhkhiuyhjvkjuiyvhjblhioguikjkly_lui" + "reCaptcha": { + "siteKey": "yjt,kugcjvhkyhgchkuyjgugjgcvkuh", + "secretKey": "yjt,vhjtfykjvhkhiuyhjvkjuiyvhjblhioguikjkly_lui" + }, + "email": { + "server": { + "user": "user", + "password": "password", + "host": "smtp.your-email.com", + "ssl": true + }, + "from": "Contact Email ", + "contact": "Your Contact " + } } diff --git a/locales/en.json b/locales/en.json index 164330b..5fcb0ff 100644 --- a/locales/en.json +++ b/locales/en.json @@ -13,8 +13,17 @@ "GEA": "Management of companies and administrations student office", "IT": "IT student office", "made": "Made with ❤️ by", - "contact": "Contact", - "orderIssue": "Order issue" + "orderIssue": "Order issue", + "phoneNumber": "Phone number", + "message": "Message", + "send": "Send", + "subject": "Subject", + "warnMessage": "Don't forget to specify the order number (s) if necessary", + "commandEdit": "Order modification", + "commandRemove": "Order deletion", + "question": "Practical question", + "profile": "My profile", + "other": "Another question" }, "index": { "welcome": "Welcome to Sandwiches Order Doua", @@ -64,5 +73,7 @@ "date": "Date", "order": "Order", "save": "Save", - "sandwich": "Sandwich" + "sandwich": "Sandwich", + "contact": "Contact", + "messageSend": "The message has been sent" } diff --git a/locales/fr.json b/locales/fr.json index 4269810..f16b6de 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -13,8 +13,17 @@ "GEA": "BDE GEA", "IT": "BDE Info", "made": "Fait avec ❤️ par", - "contact": "Contact", - "orderIssue": "Problème de commande" + "orderIssue": "Problème de commande", + "phoneNumber": "Numéro de téléphone", + "message": "Message", + "send": "Envoyer", + "subject": "Sujet", + "warnMessage": "N'oubliez pas de spécifier le/les numéro/s de commande/s si nécessaire", + "commandEdit": "Modification de commande", + "commandRemove": "Suppression de commande", + "question": "Question Pratique", + "profile": "Mon profil", + "other": "Autre question" }, "index": { "welcome": "Bienvenue sur Sandwiches Order Doua", @@ -64,5 +73,7 @@ "date": "Date", "order": "Commande", "save": "Enregistrer", - "sandwich": "Sandwich" + "sandwich": "Sandwich", + "contact": "Contact", + "messageSend": "Le message a bien été envoyé" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 27fc2ab..cfd7632 100644 --- a/package-lock.json +++ b/package-lock.json @@ -706,6 +706,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emailjs": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/emailjs/-/emailjs-3.3.0.tgz", + "integrity": "sha512-O8fUbXhyzZEokeLq+mrefYaSdToLXlW3oQfCU+75kqkwL27W8Cb6PW/ipjF1ePapLNoAvWPvwInh/x7x4z/jKA==" + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", diff --git a/package.json b/package.json index c0c465f..343b456 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "cookie-parser": "~1.4.4", "debug": "~2.6.9", + "emailjs": "^3.3.0", "express": "~4.16.1", "express-recaptcha": "^5.0.2", "express-session": "^1.17.1", diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 81c5bfa..13c6561 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -56,8 +56,7 @@ a { font-size: 100%; } -.field input[type="text"], .field input[type="list"], .field input[type="date"], .field input[type="password"], -.field input[type="number"], .field input[type="checkbox"], .field input[type="email"] { +.field input { height: 2.5vh; border-top: none; border-left: none; @@ -102,6 +101,28 @@ a { text-decoration: underline; } +#contact .cont { + display: flex; + justify-content: space-between; +} + +#contact .cont .field { + margin: 0.5em; +} + +.field.message { + width: max-content; + height: max-content; +} + +.message>textarea { + width: 60vw; + height: 30vh; + overflow: auto; + resize: none; + font-size: large; +} + .popup { position: fixed; top: 0; @@ -225,4 +246,8 @@ p.before-link a::before { body { font-size: xx-large; } + + .message>textarea { + font-size: xx-large; + } } diff --git a/routes/contact.js b/routes/contact.js new file mode 100644 index 0000000..4f041d7 --- /dev/null +++ b/routes/contact.js @@ -0,0 +1,32 @@ +let express = require("express"); +let router = express.Router(); +let error = require("./utils/error"); +let reCaptcha = require("../middlewares/reCaptcha"); +let Message = require("emailjs").Message; + + +router.post("/", reCaptcha, async (req, res) => { + if (!req.body.firstName || !req.body.lastName || !req.body.email || !req.body.subject || ! req.body.message) + return error(req, res, "Invalid contact form !", 400, "Missing arg"); + + let config = req.app.get("config"); + + req.app.get("mailClient").send( new Message({ + text: +`${req.body.firstName} ${req.body.lastName} <${req.body.email}> ${req.body.phoneNumber ? "["+req.body.phoneNumber+"] " : ""}- ${req.body.subject} + +${req.body.message}`, + from: config.email.from, + to: config.email.contact, + cc: `${req.body.firstName} ${req.body.lastName} <${req.body.email}>`, + subject: res.__("contact")+": "+req.body.subject + }), (err, message) => { + if (err) + return error(req, res, "Fail to send message !", 500, + req.app.get("env") !== "production" ? err : undefined); + else + res.render("contact", {title: "SOD - Contact"}); + }); +}); + +module.exports = router; diff --git a/views/contact.pug b/views/contact.pug new file mode 100644 index 0000000..06c785a --- /dev/null +++ b/views/contact.pug @@ -0,0 +1,6 @@ +extends layout + +block content + div.card + h1=__("contact") + p=__("messageSend") diff --git a/views/layout.pug b/views/layout.pug index 4191441..31c1dc8 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -30,7 +30,7 @@ html div#more a=__("layout.about") - a=__("layout.contact") + a=__("contact") div#dark.hide div#about.popup.card.hide @@ -58,20 +58,36 @@ html p.before-link=__("layout.made") a(href="https://www.linkedin.com/in/florian-charlaix" target="_blank") Florian Charlaix div#contact.popup.card.hide - h1=__("layout.contact") - p.before-link=__("layout.orderIssue") + ":" - a(href="mailto: ") test@test.fr - p.before-link=__("layout.bio") + ":" - a(href="mailto: ") - p.before-link=__("layout.chemistry") + ":" - a(href="mailto: ") - p.before-link=__("layout.GC") + ":" - a(href="mailto: ") - p.before-link=__("layout.GCPD") + ":" - a(href="mailto: bde.gcgp.lyon1@gmail.com") bde.gcgp.lyon1@gmail.com - p.before-link=__("layout.GEA") + ":" - a(href="mailto: ") - p.before-link=__("layout.IT") + ":" - a(href="mailto: contact@bde-info.org") contact@bde-info.org + h1=__("contact") + form(action="/contact" method="POST") + div.cont + div.field + label(for="firstNameContact")="* "+__("firstName") + input#firstNameContact(type="text" name="firstName" value=user?user.firstName:"" required) + div.field + label(for="lastNameContact")="* "+__("lastName") + input#lastNameContact(type="text" name="lastName" value=user ? user.lastName : "" required) + div.cont + div.field + label(for="emailContact")="* "+__("email") + input#emailContact(type="email" name="email" value=user ? user.email : "" required) + div.field + label(for="phoneNumberContact")=__("layout.phoneNumber") + input#phoneNumberContact(type="tel" name="phoneNumber") + div.field + label(for="subjectContact")="* "+__("layout.subject") + input#subjectContact(list="subjectContactList" autocomplete="off" name="subject" required) + datalist#subjectContactList + option(value=__("layout.commandEdit")) + option(value=__("layout.commandRemove")) + option(value=__("layout.question")) + option(value=__("layout.profile")) + option(value=__("layout.other")) + p=__("layout.warnMessage") + div.field.message + label(for="messageContact")=__("layout.message") + textarea#messageContact(name="message" required) + div.field + +submit(value=__("layout.send")) script(src="/javascripts/layout.js")