Last active
May 17, 2017 17:25
-
-
Save ccjmne/096a1e2e8198d0efc2a6d93178b5261e to your computer and use it in GitHub Desktop.
AWS Lambda function for NCLSDevelopment's Orca solution - uses SES to send a confirmation email to a given address
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AWS = require('aws-sdk'); | |
ses = new AWS.SES(); | |
const debug = false; | |
// Base64 'Orca — Comptabilité' is 'T3JjYSDigJQgQ29tcHRhYmlsaXTDqQ==' | |
const Source_64 = 'T3JjYSDigJQgQ29tcHRhYmlsaXTDqQ=='; | |
function _proxyLambdaCallback(context, status, body) { | |
// TODO: what context method to use on failure? | |
(status >= 200 && status < 300 ? context.succeed : context.succeed)({ | |
statusCode: status, | |
headers: { | |
'Access-Control-Allow-Methods': 'GET, OPTIONS', | |
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', | |
'Access-Control-Allow-Origin': '*' | |
}, | |
body: JSON.stringify(body) | |
}); | |
} | |
function _error(context, callback, reason) { | |
_proxyLambdaCallback(context, 400, { | |
outcome: 'failure', | |
reason: reason | |
}); | |
callback(reason); | |
} | |
function _success(context, callback, recipient) { | |
_proxyLambdaCallback(context, 200, { | |
outcome: 'success', | |
recipient: recipient | |
}); | |
callback(null, 'mailto completed successfully -- email sent to ' + recipient); | |
} | |
function send(recipient, messageTitle, messageBody, callback) { | |
ses.sendEmail({ | |
Source: '"=?utf-8?B?' + Source_64 + '?=" <accounting@orca-solution.com>', | |
Destination: { | |
ToAddresses: [recipient] | |
}, | |
ReturnPath: 'nclsdevelopment@gmail.com', | |
Message: { | |
Subject: { | |
Charset: 'UTF-8', | |
Data: messageTitle | |
}, | |
Body: { | |
Html: { | |
Charset: 'UTF-8', | |
Data: messageBody | |
} | |
} | |
} | |
}, callback); | |
} | |
function notifyMe(recipient, invoice, callback) { | |
ses.sendEmail({ | |
Source: '"=?utf-8?B?' + Source_64 + '?=" <accounting@orca-solution.com>', | |
Destination: { | |
ToAddresses: ['nclsdevelopment@gmail.com'] | |
}, | |
Message: { | |
Subject: { | |
Charset: 'UTF-8', | |
Data: 'Nouvelle prise de contact -- ' + recipient.organization | |
}, | |
Body: { | |
Html: { | |
Charset: 'UTF-8', | |
Data: '\ | |
<table> \ | |
<tbody> \ | |
<tr><td style="padding: 3px 5px">Nom</td><td style="padding: 3px 5px"><code style="font-size: 1.2em;">' + recipient.name + '</code></td></tr> \ | |
<tr><td style="padding: 3px 5px">Organisation</td><td style="padding: 3px 5px"><code style="font-size: 1.2em;">' + recipient.organization + '</code></td></tr> \ | |
<tr><td style="padding: 3px 5px">Courriel</td><td style="padding: 3px 5px"><code style="font-size: 1.2em;">' + recipient.address + '</code></td></tr> \ | |
</tbody> \ | |
</table> \ | |
' + invoice | |
} | |
} | |
} | |
}, callback); | |
} | |
function _compute(value, yearly, discount) { | |
return yearly ? value * (discount ? 11 : 12) : value; | |
} | |
function _center(content) { | |
return ' \ | |
<table width="100%" border="0" cellspacing="0" cellpadding="0"> \ | |
<tr> \ | |
<td align="center"> \ | |
' + content + ' \ | |
</td> \ | |
</tr> \ | |
</table>'; | |
} | |
exports.handler = (event, context, callback) => { | |
const error = _error.bind(_error, context, callback), | |
success = _success.bind(_success, context, callback); | |
function mock(body) { | |
context.succeed({ | |
statusCode: 200, | |
headers: { | |
'Access-Control-Allow-Methods': 'GET, OPTIONS', | |
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', | |
'Access-Control-Allow-Origin': '*' | |
}, | |
body: body | |
}); | |
callback(null, '`mailme` mocked successfully'); | |
} | |
var body; | |
try { | |
body = JSON.parse(event.body); | |
} catch (e) { | |
error('Invalid payload -- expected JSON-formatted data'); | |
return; | |
} | |
const plan = body.plan, | |
recipient = body.recipient; | |
const hasOwnProperties = (object, properties) => properties.reduce((agg, cur) => agg && object.hasOwnProperty(cur), true); | |
if (!recipient || !plan || !hasOwnProperties(recipient, ['name', 'organization', 'address']) || !hasOwnProperties(plan, ['name', 'display', 'base', 'yearly']) || (plan.name === | |
'business' && !hasOwnProperties(plan, ['upper', 'extra']))) { | |
error('Bad argument -- not enough information provided'); | |
return; | |
} | |
const messageTitle = 'Acquisition de la solution Orca'; | |
var messageBody = _center( | |
'\ | |
<img src="https://s3-eu-west-1.amazonaws.com/orca-resources/logo_complete.png" style="max-height: 60px" /> \ | |
<h1 style="font-weight: lighter;">Orca — Gestion des Formations Sécurité</h1>' | |
); | |
messageBody += '\ | |
<hr /> \ | |
<p style="text-align: justify;"> \ | |
À l\'attention de <em style="background-color: yellow;">' + recipient.name + '</em>. \ | |
<br />Vous recevez ce message car vous avez exprimé de l\'intérêt pour la solution Orca et envisagez de l\'acquérir pour votre organisation : <em style="background-color: yellow;">' + recipient.organization + '</em>. \ | |
<br />Voici le détail du modèle sur lequel votre attention s\'est portée : \ | |
</p> \ | |
<h2 style="border-bottom: 1px dashed grey;">Solution Orca — modèle <em>' + plan.display + '</em></h2>'; | |
messageBody += { | |
micro: '\ | |
<p style="text-align: justify;"> \ | |
Le modèle <em>Micro</em> est le plan le moins onéreux pour la solution Orca. \ | |
<br /> Il permet aux petites entités d\'accéder à la solution à un moindre prix. Sa particularité essentielle concerne la limite \ | |
du nombre d\'agents gérés : pas plus de 500. Comme les deux autres plans, il offre un accès à un nombre illimité d\'utilisateurs \ | |
à tout moment. Sa configuration matérielle est amplement suffisante pour convenir même à un usage intensif et ce modèle \ | |
inclut également l\'accès à toutes les mises à jour de la solution, majeures comme mineures. \ | |
</p>', | |
business: '\ | |
<p style="text-align: justify;"> \ | |
Le plan <em>Business</em> est le modèle standard pour la solution Orca. \ | |
<br />Il offre une configuration matérielle amplement suffisante pour convenir même à un usage intensif. Les données sont répliquées \ | |
et sauvegardées quotidiennement — avec une semaine de disponibilité. Ce modèle inclue également l\'accès à toutes \ | |
les mises à jour de la solution, majeures comme mineures. \ | |
</p>', | |
partner: '\ | |
<p style="text-align: justify;"> \ | |
Le plan <em>Partner</em> est le modèle haut-de-gamme pour la solution Orca. \ | |
<br />Il offre, en plus des fonctionnalités du plan <em>Business</em> : \ | |
</p> \ | |
<ol type="I"> \ | |
<li>une configuration matérielle dédiée et de haute performance</li> \ | |
<li>un monitoring des tentatives d\'intrusion</li> \ | |
<li>un nombre d\'agents actifs et comptes utilisateurs illimités</li> \ | |
<li>un accès participatif au tableau de bord de développement de la solution</li> \ | |
</ol> \ | |
<p style="text-align: justify;"> \ | |
Avec ce plan, soyez impliqués dans l\'évolution d\'Orca en bénéficiant d\'un contact direct avec l\'équipe de développement. Discutez des améliorations \ | |
que vous voudriez voir apparaître et suivez en détail l\'avancement de la résolution de bugs ou de l\'intégration de nouvelles fonctionnalités. \ | |
</p>' | |
}[plan.name]; | |
const invoice = '\ | |
<h2 style="border-bottom: 1px dashed grey;">Estimation de votre devis</h2>' | |
+ _center('\<table style="max-width: 600px; border: 1px solid lightgrey; border-radius: 2px; border-collapse: collapse; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2)"> \ | |
<thead style="background-color: #eee; border-bottom: 1px solid lightgrey;"> \ | |
<tr> \ | |
<th style="padding: 5px 16px;">Description</th> \ | |
<th style="padding: 5px 16px;" width="1%">Montant ' + (plan.yearly ? 'annuel' : 'mensuel') + '</th> \ | |
</tr> \ | |
</thead> \ | |
<tbody> \ | |
<tr> \ | |
<td style="padding: 5px 16px;">Modèle <em>' + plan.display + '</em></td> \ | |
<td style="padding: 5px 16px; text-align: right;">+ ' + _compute(plan.base, plan.yearly) + '€</td> \ | |
</tr> \ | |
<tr style="display: ' + (plan.name === 'business' ? 'visible' : 'none') + ';"> \ | |
<td style="padding: 5px 16px;">Jusqu\'à <em>' + plan.upper + '</em> agents actifs</td> \ | |
<td style="padding: 5px 16px; text-align: right;">+ ' + _compute(plan.extra, plan.yearly) + '€</td> \ | |
</tr> \ | |
<tr style="background-color: #eee; border-top: 1px solid lightgrey; border-bottom: 1px solid lightgrey; display: ' + (plan.name === 'business' && plan.yearly ? 'visible' : 'none') + ';"> \ | |
<td style="padding: 5px 16px; text-align: right;">Sous-total</td> \ | |
<td style="padding: 5px 16px; text-align: right;">+ ' + _compute(plan.base + (plan.name === 'business' ? plan.extra : 0), plan.yearly) + '€</td> \ | |
</tr> \ | |
<tr style="display: ' + (plan.yearly ? 'visible' : 'none') + ';"> \ | |
<td style="padding: 5px 16px;">Facturation <em>annuelle</em></td> \ | |
<td style="padding: 5px 16px; text-align: right;">− ' + plan.base + '€</td> \ | |
</tr> \ | |
<tr style="background-color: #eee; border-top: 1px solid lightgrey;"> \ | |
<td style="padding: 5px 16px; text-align: right;">Total ' + (plan.yearly ? 'annuel' : 'mensuel') + '</td> \ | |
<td style="padding: 5px 16px; text-align: right;">+ ' + _compute(plan.base + (plan.name === 'business' ? plan.extra : 0), plan.yearly, true) + '€</td> \ | |
</tr> \ | |
</tbody> \ | |
</table> \ | |
<small style="float: right;">Prix exprimés hors taxe</small>'); | |
messageBody += invoice; | |
messageBody += '\ | |
<p style="text-align: justify;"> \ | |
Merci pour l\'intérêt que vous portez à cette solution. \ | |
<br />Notre équipe prendra contact avec vous dès que possible pour discuter de votre projet. \ | |
</p> \ | |
<hr /> \ | |
<p><small>Ceci est un e-mail automatisé — ne pas y répondre.</small></p>'; | |
messageBody = '<div style="font-family: sans-serif;">' + messageBody + '</div>'; | |
if (debug) { | |
//notifyMe(recipient, invoice, (err, data) => err ? error(err) : success(data)); | |
mock(messageBody); | |
} else { | |
send(recipient.address, messageTitle, messageBody, function (err, data) { | |
if (err) { | |
error(err); | |
return; | |
} | |
notifyMe(recipient, invoice, success.bind(success, recipient.address)); | |
}); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment