#40 - Formulaire de contact via SMTP #58
9 changed files with 134 additions and 10 deletions
11
README.md
11
README.md
|
@ -18,7 +18,9 @@ Vous pouvez, si vous le souhaitez héberger l'application sur votre propre serve
|
|||
|
||||
### Prérequis
|
||||
|
||||
Il existe 2 méthodes d'installation, soit via docker soit en mode standalone. Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle.
|
||||
Il existe 2 méthodes d'installation, soit via docker soit en mode standalone.
|
||||
|
||||
Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle ou configurer le SMTP tel que défini dans la section [variables d'environnements](#env-file).
|
||||
|
||||
Pour la méthode docker il ne vous faudra rien de plus que `docker` et `docker-compose`.
|
||||
|
||||
|
@ -227,6 +229,13 @@ S3_BUCKET # Nom du bucket (musictopus par défaut, à changer impérativement si
|
|||
JOBS_HEADER_KEY # Nom du header utilisé pour l'identification des tâches cron (musictopus par défaut)
|
||||
JOBS_HEADER_VALUE # Valeur de la clé (ooYee9xok7eigo2shiePohyoGh1eepew par défaut)
|
||||
REGISTRATION_OPEN # true/false en fonction de si vous souhaitez activer ou non l'inscription à votre instance (true par défaut)
|
||||
MAIL_METHOD # permet de définir la façon dont les mails de la page contact sont envoyés (formspree ou smtp)
|
||||
MAIL_HOST # Adresse du server mail (dams le cas ou MAIL_METHOD est défini sur smtp)
|
||||
MAIL_PORT # Port d'écoute du serveur smtp (dams le cas ou MAIL_METHOD est défini sur smtp)
|
||||
MAIL_USER # Adresse mail du compte permettant d'envoyer les mails (dams le cas ou MAIL_METHOD est défini sur smtp)
|
||||
MAIL_PASSWORD # Mot de passe du compte email (dams le cas ou MAIL_METHOD est défini sur smtp)
|
||||
MAIL_TO # Adresse mail du contact qui recevra les messages de la page "nous contacter" (dams le cas ou MAIL_METHOD est défini sur smtp)
|
||||
|
||||
```
|
||||
|
||||
## Contributeurs
|
||||
|
|
|
@ -37,6 +37,12 @@ services:
|
|||
JOBS_HEADER_KEY: ${JOBS_HEADER_KEY}
|
||||
JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE}
|
||||
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
|
||||
MAIL_METHOD: ${MAIL_METHOD}
|
||||
MAIL_HOST: ${MAIL_HOST}
|
||||
MAIL_PORT: ${MAIL_PORT}
|
||||
MAIL_USER: ${MAIL_USER}
|
||||
MAIL_PASSWORD: ${MAIL_PASSWORD}
|
||||
MAIL_TO: ${MAIL_TO}
|
||||
networks:
|
||||
- musictopus
|
||||
musictopus-db:
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"knacss": "^8.0.4",
|
||||
"mongoose": "^6.2.1",
|
||||
"mongoose-unique-validator": "^3.0.0",
|
||||
"nodemailer": "^6.7.8",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"passport": "^0.5.2",
|
||||
"passport-custom": "^1.1.1",
|
||||
|
|
|
@ -23,6 +23,7 @@ import importJobsRouter from "./routes/jobs";
|
|||
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
|
||||
import importSearchRouterApiV1 from "./routes/api/v1/search";
|
||||
import importMeRouterApiV1 from "./routes/api/v1/me";
|
||||
import importContactRouterApiV1 from "./routes/api/v1/contact";
|
||||
|
||||
passportConfig(passport);
|
||||
|
||||
|
@ -91,6 +92,7 @@ app.use("/jobs", importJobsRouter);
|
|||
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
||||
app.use("/api/v1/search", importSearchRouterApiV1);
|
||||
app.use("/api/v1/me", importMeRouterApiV1);
|
||||
app.use("/api/v1/contact", importContactRouterApiV1);
|
||||
|
||||
// Handle 404
|
||||
app.use((req, res) => {
|
||||
|
|
|
@ -19,4 +19,14 @@ module.exports = {
|
|||
process.env.JOBS_HEADER_VALUE || "ooYee9xok7eigo2shiePohyoGh1eepew",
|
||||
registrationOpen:
|
||||
(process.env.REGISTRATION_OPEN || "true").toLowerCase() === "true",
|
||||
mailMethod: process.env.MAIL_METHOD || "formspree",
|
||||
smtpConfig: {
|
||||
host: process.env.MAIL_HOST,
|
||||
port: process.env.MAIL_PORT,
|
||||
auth: {
|
||||
user: process.env.MAIL_USER,
|
||||
pass: process.env.MAIL_PASSWORD,
|
||||
},
|
||||
},
|
||||
mailTo: process.env.MAIL_TO,
|
||||
};
|
||||
|
|
50
src/routes/api/v1/contact.js
Normal file
50
src/routes/api/v1/contact.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import express from "express";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
import { sendResponse } from "../../../libs/format";
|
||||
|
||||
import { mailMethod, smtpConfig, mailTo, siteName } from "../../../config";
|
||||
import ErrorEvent from "../../../libs/error";
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/").post(async (req, res, next) => {
|
||||
try {
|
||||
if (mailMethod === "smtp") {
|
||||
const { email, name, message } = req.body;
|
||||
|
||||
if (!email || !message) {
|
||||
throw new ErrorEvent(
|
||||
406,
|
||||
"Le formulaire n'est pas correctement saisi"
|
||||
);
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport(smtpConfig);
|
||||
|
||||
const text = `Bonjour,
|
||||
Vous venez de recevoir un nouveau message de ${name} (${email}) :
|
||||
|
||||
${message}
|
||||
`;
|
||||
|
||||
const data = await transporter.sendMail({
|
||||
from: smtpConfig.auth.user,
|
||||
to: mailTo,
|
||||
subject: `${siteName} : Nouveau message`,
|
||||
text,
|
||||
});
|
||||
|
||||
const { messageId, response } = data;
|
||||
|
||||
return sendResponse(req, res, { messageId, response });
|
||||
}
|
||||
|
||||
throw new ErrorEvent(500, "Méthode non configurée");
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -185,7 +185,6 @@
|
|||
const entries = urlParams.entries();
|
||||
|
||||
for(const entry of entries) {
|
||||
console.log(`${entry[0]}: ${entry[1]}`);
|
||||
switch(entry[0]) {
|
||||
case 'artists_sort':
|
||||
this.artist = entry[1];
|
||||
|
|
|
@ -239,7 +239,6 @@
|
|||
const entries = urlParams.entries();
|
||||
|
||||
for(const entry of entries) {
|
||||
console.log(`${entry[0]}: ${entry[1]}`);
|
||||
switch(entry[0]) {
|
||||
case 'artists_sort':
|
||||
this.artist = entry[1];
|
||||
|
|
|
@ -1,22 +1,70 @@
|
|||
<section class="box">
|
||||
<section class="box" id="app">
|
||||
<h1>Nous contacter</h1>
|
||||
<form action="https://formspree.io/f/<%= config.formspreeId %>" method="POST">
|
||||
<form @submit="send" <% if (config.mailMethod === 'formspree' ) { %> id="contact" method="POST" action="https://formspree.io/f/<%= config.formspreeId %>" <% } %>>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-16">
|
||||
<div class="field">
|
||||
<label for="email">Addresse e-mail*</label>
|
||||
<input type="email" name="email" id="email" required />
|
||||
<input type="email" name="email" id="email" v-model="email" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="name">Prénom, nom</label>
|
||||
<input type="text" name="name" id="name" />
|
||||
<input type="text" name="name" id="name" v-model="name" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="message">Message*</label>
|
||||
<textarea name="message" id="message" rows="6" required ></textarea>
|
||||
<textarea name="message" id="message" rows="6" required v-model="message" ></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button is-primary">Envoyer</button>
|
||||
<button type="submit" class="button is-primary" :disabled="loading">
|
||||
<% if (config.mailMethod !== 'formspree' ) { %>
|
||||
<i class="icon-spin animate-spin" v-if="loading"></i>
|
||||
<% } %>
|
||||
Envoyer
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<% if (config.mailMethod === 'smtp' ) { %>
|
||||
<script>
|
||||
Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
name: '',
|
||||
message: '',
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
send(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if ( this.loading ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const {
|
||||
email,
|
||||
message,
|
||||
name
|
||||
} = this;
|
||||
|
||||
axios.post('/api/v1/contact', {email, name, message})
|
||||
.then( () => {
|
||||
showToastr("Message correctement envoyé", true);
|
||||
})
|
||||
.catch((err) => {
|
||||
showToastr(err.response?.data?.message || "Impossible d'envoyer votre message", false);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
},
|
||||
}).mount('#app');
|
||||
</script>
|
||||
<% } %>
|
Loading…
Reference in a new issue