diff --git a/package-lock.json b/package-lock.json index 754579aa2c53689cc4ae5034d0355c06de0b36e6..eed96a574a6edf78eb72718c3ba5a1920c8cfec0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5809,7 +5809,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -6615,8 +6614,7 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "p-defer": { "version": "1.0.0", @@ -6990,8 +6988,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.1.31", @@ -8989,6 +8986,14 @@ "next-tick": "1" } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -9840,6 +9845,15 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + } + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -10678,8 +10692,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { "version": "11.1.0", diff --git a/package.json b/package.json index a4b40f76a89336a1bd5b4fc4d6ee7d1da7bebd93..fe1da5813980e4e7d7b12f3f8d6d60eb917cef4f 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "moment": "^2.24.0", "reflect-metadata": "^0.1.12", "rxjs": "^6.2.2", - "typescript": "^3.0.1" + "typescript": "^3.0.1", + "useragent": "^2.3.0" }, "devDependencies": { "@nestjs/testing": "^5.1.0", diff --git a/src/email-templates/feedback/feedback.html b/src/email-templates/feedback/feedback.html new file mode 100644 index 0000000000000000000000000000000000000000..c3a6d481295248be1353a79b84a73dcb617fe42d --- /dev/null +++ b/src/email-templates/feedback/feedback.html @@ -0,0 +1,45 @@ +<!doctype html><html lang="fr" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><title>Feedback</title><!--[if !mso]><!-- --><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style type="text/css">#outlook a { padding:0; } + .ReadMsgBody { width:100%; } + .ExternalClass { width:100%; } + .ExternalClass * { line-height:100%; } + body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; } + table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; } + img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; } + p { display:block;margin:13px 0; }</style><!--[if !mso]><!--><style type="text/css">@media only screen and (max-width:480px) { + @-ms-viewport { width:320px; } + @viewport { width:320px; } + }</style><!--<![endif]--><!--[if mso]> + <xml> + <o:OfficeDocumentSettings> + <o:AllowPNG/> + <o:PixelsPerInch>96</o:PixelsPerInch> + </o:OfficeDocumentSettings> + </xml> + <![endif]--><!--[if lte mso 11]> + <style type="text/css"> + .outlook-group-fix { width:100% !important; } + </style> + <![endif]--><!--[if !mso]><!--><link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet" type="text/css"><style type="text/css">@import url(https://fonts.googleapis.com/css?family=Lato);</style><!--<![endif]--><style type="text/css">@media only screen and (min-width:480px) { + .mj-column-per-100 { width:100% !important; max-width: 100%; } +.mj-column-per-50 { width:50% !important; max-width: 50%; } + }</style><style type="text/css">@media only screen and (max-width:480px) { + table.full-width-mobile { width: 100% !important; } + td.full-width-mobile { width: auto !important; } + }</style><style type="text/css">.warn-message { + color: "white"; + } + + .link { + color: white; + font-weight: bold; + } + + .pre { + white-space: pre; + } + + @media (max-width:480px) { + .title { + padding: 7% 10% !important; + } + }</style></head><body><div class="body"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:0;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#333744;background-color:#333744;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#333744;background-color:#333744;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:0;line-height:0;text-align:left;display:inline-block;width:100%;direction:ltr;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:394px;" ><![endif]--><div class="mj-column-per-50 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="left" style="font-size:0px;padding:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:130px;"><img alt="Logo data.grandlyon.com" height="auto" src="${options.imageHost}/logo_data_neg.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="130"></td></tr></tbody></table></td></tr></table></div><!--[if mso | IE]></td><td style="vertical-align:top;width:394px;" ><![endif]--><div class="mj-column-per-50 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="right" style="font-size:0px;padding:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:80px;"><img alt="Logo Grand Lyon" height="auto" src="${options.imageHost}/logo_gl_négatif.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="80"></td></tr></tbody></table></td></tr></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#f2f2f2;background-color:#f2f2f2;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#f2f2f2;background-color:#f2f2f2;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tbody><tr><td style="background-color:white;vertical-align:top;padding:3% 6%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tr><td align="center" class="title" style="font-size:0px;padding:3% 10%;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:18px;font-weight:bold;line-height:18px;text-align:center;color:#333744;">Feedback</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:left;color:#333744;">Un nouveau feedback a été déposée le ${options.datetime}.</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:left;color:#333744;">Voici un résumé de la demande :</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Url:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.url}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Version:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.version}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">User-Agent:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.userAgent}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Message:</div></td></tr><tr><td align="left" class="pre" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.message}</div></td></tr><tr><td align="right" style="font-size:0px;padding:3% 0 0 0;padding-top:3%;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:right;color:#333744;">L'équipe data.grandlyon.com</div></td></tr><tr><td align="right" style="font-size:0px;padding:10px 25px;padding-top:1%;padding-right:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:105px;"><img alt="Logo data.grandlyon.com" height="auto" src="${options.imageHost}/logo_data.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="105"></td></tr></tbody></table></td></tr></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#333744;background-color:#333744;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#333744;background-color:#333744;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="center" style="font-size:0px;padding:0;word-break:break-word;"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="https://download.data.grandlyon.com/catalogue/srv/fre/rss.search?sortBy=publicationDate&georss=simplepoint&data" target="_blank"><img alt="Logo rss" height="16" src="${options.imageHost}/logo_rss.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="http://www.facebook.com/legrandlyon" target="_blank"><img alt="Logo Facebook" height="16" src="${options.imageHost}/logo_fb.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="https://twitter.com/grandlyon" target="_blank"><img alt="Logo Twitter" height="16" src="${options.imageHost}/logo_twi.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div></body></html> \ No newline at end of file diff --git a/src/email-templates/feedback/feedback.mjml b/src/email-templates/feedback/feedback.mjml new file mode 100644 index 0000000000000000000000000000000000000000..f008514f31926b3ee5f542996a87a7df0aabf5bd --- /dev/null +++ b/src/email-templates/feedback/feedback.mjml @@ -0,0 +1,106 @@ +<mjml lang="fr"> + <mj-head> + <mj-title>Feedback</mj-title> + <mj-font name="Lato" href="https://fonts.googleapis.com/css?family=Lato" /> + <mj-attributes> + <mj-text font-family="Lato, sans-serif" line-height="18px" font-size="14px" padding="3% 0 0 0" color="#333744"></mj-text> + <mj-social-element padding="0 5px 0 5px"></mj-social-element> + </mj-attributes> + <mj-style> + .warn-message { + color: "white"; + } + + .link { + color: white; + font-weight: bold; + } + + .pre { + white-space: pre; + } + + @media (max-width:480px) { + .title { + padding: 7% 10% !important; + } + } + </mj-style> + </mj-head> + <mj-body width="800px" css-class="body"> + <mj-wrapper padding="0"> + <mj-section background-color="#333744" padding="3% 6%"> + <mj-group> + <mj-column> + <mj-image width="130px" align="left" padding="0" alt="Logo data.grandlyon.com" src="${options.imageHost}/logo_data_neg.png"></mj-image> + </mj-column> + <mj-column> + <mj-image width="80px" align="right" padding="0" alt="Logo Grand Lyon" src="${options.imageHost}/logo_gl_négatif.png"></mj-image> + </mj-column> + </mj-group> + </mj-section> + + <mj-section background-color="#f2f2f2" padding="3% 6%"> + <mj-column background-color="white" padding="3% 6%"> + <mj-text align="center" css-class="title" font-size="18px" font-weight="bold" padding="3% 10%"> + Feedback + </mj-text> + <mj-text> + Un nouveau feedback a été déposée le ${options.datetime}. + </mj-text> + + <mj-text> + Voici un résumé de la demande : + </mj-text> + <mj-text font-weight="bold"> + Url: + </mj-text> + + <mj-text font-style="italic"> + ${options.url} + </mj-text> + + <mj-text font-weight="bold"> + Version: + </mj-text> + + <mj-text font-style="italic">${options.version}</mj-text> + + <mj-text font-weight="bold"> + User-Agent: + </mj-text> + + <mj-text font-style="italic"> + ${options.userAgent} + </mj-text> + + <mj-text font-weight="bold"> + Message: + </mj-text> + + <mj-text css-class="pre" font-style="italic"> + ${options.message} + </mj-text> + + <mj-text align="right" padding-top="3%"> + L'équipe data.grandlyon.com + </mj-text> + <mj-image align="right" width="105px" padding-right="0" padding-top="1%" alt="Logo data.grandlyon.com" src="${options.imageHost}/logo_data.png"></mj-image> + </mj-column> + </mj-section> + + <mj-section background-color="#333744" padding="3% 6%"> + <mj-column> + <mj-social padding="0" font-size="15px" icon-size="16px" mode="horizontal"> + <mj-social-element alt="Logo rss" href="https://download.data.grandlyon.com/catalogue/srv/fre/rss.search?sortBy=publicationDate&georss=simplepoint&data" src="${options.imageHost}/logo_rss.png"> + </mj-social-element> + <mj-social-element alt="Logo Facebook" href="http://www.facebook.com/legrandlyon" src="${options.imageHost}/logo_fb.png"> + </mj-social-element> + <mj-social-element alt="Logo Twitter" href="https://twitter.com/grandlyon" src="${options.imageHost}/logo_twi.png"> + </mj-social-element> + </mj-social> + </mj-column> + </mj-section> + </mj-wrapper> + </mj-body> +</mjml> \ No newline at end of file diff --git a/src/email-templates/feedback/index.ts b/src/email-templates/feedback/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1135ae05410eedbfc9d99b3559750c5d7b418849 --- /dev/null +++ b/src/email-templates/feedback/index.ts @@ -0,0 +1,58 @@ +export const buildFeedbackEmail = (options: FeedbackEmailBodyOptions) => { + const html = `<!doctype html><html lang="fr" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><title>Feedback</title><!--[if !mso]><!-- --><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style type="text/css">#outlook a { padding:0; } + .ReadMsgBody { width:100%; } + .ExternalClass { width:100%; } + .ExternalClass * { line-height:100%; } + body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; } + table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; } + img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; } + p { display:block;margin:13px 0; }</style><!--[if !mso]><!--><style type="text/css">@media only screen and (max-width:480px) { + @-ms-viewport { width:320px; } + @viewport { width:320px; } + }</style><!--<![endif]--><!--[if mso]> +<xml> +<o:OfficeDocumentSettings> + <o:AllowPNG/> + <o:PixelsPerInch>96</o:PixelsPerInch> +</o:OfficeDocumentSettings> +</xml> +<![endif]--><!--[if lte mso 11]> +<style type="text/css"> + .outlook-group-fix { width:100% !important; } +</style> +<![endif]--><!--[if !mso]><!--><link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet" type="text/css"><style type="text/css">@import url(https://fonts.googleapis.com/css?family=Lato);</style><!--<![endif]--><style type="text/css">@media only screen and (min-width:480px) { +.mj-column-per-100 { width:100% !important; max-width: 100%; } +.mj-column-per-50 { width:50% !important; max-width: 50%; } +}</style><style type="text/css">@media only screen and (max-width:480px) { +table.full-width-mobile { width: 100% !important; } +td.full-width-mobile { width: auto !important; } +}</style><style type="text/css">.warn-message { +color: "white"; +} + +.link { +color: white; +font-weight: bold; +} + +.pre { +white-space: pre; +} + +@media (max-width:480px) { +.title { + padding: 7% 10% !important; +} +}</style></head><body><div class="body"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:0;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#333744;background-color:#333744;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#333744;background-color:#333744;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:0;line-height:0;text-align:left;display:inline-block;width:100%;direction:ltr;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:394px;" ><![endif]--><div class="mj-column-per-50 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="left" style="font-size:0px;padding:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:130px;"><img alt="Logo data.grandlyon.com" height="auto" src="${options.imageHost}/logo_data_neg.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="130"></td></tr></tbody></table></td></tr></table></div><!--[if mso | IE]></td><td style="vertical-align:top;width:394px;" ><![endif]--><div class="mj-column-per-50 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="right" style="font-size:0px;padding:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:80px;"><img alt="Logo Grand Lyon" height="auto" src="${options.imageHost}/logo_gl_négatif.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="80"></td></tr></tbody></table></td></tr></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#f2f2f2;background-color:#f2f2f2;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#f2f2f2;background-color:#f2f2f2;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tbody><tr><td style="background-color:white;vertical-align:top;padding:3% 6%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tr><td align="center" class="title" style="font-size:0px;padding:3% 10%;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:18px;font-weight:bold;line-height:18px;text-align:center;color:#333744;">Feedback</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:left;color:#333744;">Un nouveau feedback a été déposée le ${options.datetime}.</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:left;color:#333744;">Voici un résumé de la demande :</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Url:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.url}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Version:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.version}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">User-Agent:</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.userAgent}</div></td></tr><tr><td align="left" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-weight:bold;line-height:18px;text-align:left;color:#333744;">Message:</div></td></tr><tr><td align="left" class="pre" style="font-size:0px;padding:3% 0 0 0;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;font-style:italic;line-height:18px;text-align:left;color:#333744;">${options.message}</div></td></tr><tr><td align="right" style="font-size:0px;padding:3% 0 0 0;padding-top:3%;word-break:break-word;"><div style="font-family:Lato, sans-serif;font-size:14px;line-height:18px;text-align:right;color:#333744;">L'équipe data.grandlyon.com</div></td></tr><tr><td align="right" style="font-size:0px;padding:10px 25px;padding-top:1%;padding-right:0;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:105px;"><img alt="Logo data.grandlyon.com" height="auto" src="${options.imageHost}/logo_data.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="105"></td></tr></tbody></table></td></tr></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr><tr><td class="" width="800px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:800px;" width="800" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#333744;background-color:#333744;Margin:0px auto;max-width:800px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#333744;background-color:#333744;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:3% 6%;text-align:center;vertical-align:top;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:788px;" ><![endif]--><div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tr><td align="center" style="font-size:0px;padding:0;word-break:break-word;"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="https://download.data.grandlyon.com/catalogue/srv/fre/rss.search?sortBy=publicationDate&georss=simplepoint&data" target="_blank"><img alt="Logo rss" height="16" src="${options.imageHost}/logo_rss.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="http://www.facebook.com/legrandlyon" target="_blank"><img alt="Logo Facebook" height="16" src="${options.imageHost}/logo_fb.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td><td><![endif]--><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tr><td style="padding:0 5px 0 5px;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-radius:3px;width:16px;"><tr><td style="font-size:0;height:16px;vertical-align:middle;width:16px;"><a href="https://twitter.com/grandlyon" target="_blank"><img alt="Logo Twitter" height="16" src="${options.imageHost}/logo_twi.png" style="border-radius:3px;" width="16"></a></td></tr></table></td></tr></table><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div></body></html>`; + + return html; +}; + +class FeedbackEmailBodyOptions { + url: string; + message: string; + version: string; + userAgent: string; + datetime: string; + imageHost: string; +} \ No newline at end of file diff --git a/src/email/email.controller.ts b/src/email/email.controller.ts index 405ea0f386689bf57f291ea68719ba357ab1cdb2..c0ee1f52b793eec4d23989bccd0d58d438032b23 100644 --- a/src/email/email.controller.ts +++ b/src/email/email.controller.ts @@ -1,5 +1,5 @@ -import { Controller, Post, Body, InternalServerErrorException, Logger, HttpCode } from '@nestjs/common'; -import { ContactForm, EmailWithoutFrom } from './email'; +import { Controller, Post, Body, InternalServerErrorException, Logger, HttpCode, Req } from '@nestjs/common'; +import { ContactForm, EmailWithoutFrom, FeedbackForm } from './email'; import { EmailService } from './email.service'; import { ApiBadRequestResponse, ApiOkResponse, ApiUseTags, ApiOperation, ApiInternalServerErrorResponse } from '@nestjs/swagger'; import { Groups } from '../decorators/groups.decorators'; @@ -28,6 +28,22 @@ export class EmailController { } } + @Post('feedback') + // tslint:disable-next-line:max-line-length + @ApiOperation({ title: 'Send email to admin with the user feedback' }) + @ApiOkResponse({ description: 'OK' }) + @ApiBadRequestResponse({ description: 'Missing fields' }) + @ApiInternalServerErrorResponse({ description: 'Internal error, this is probably a rabbitMQ related error (unreachable service...)'}) + @HttpCode(200) + async sendFeedback(@Body() feedbackForm: FeedbackForm, @Req() req) { + try { + return await this.emailService.sendFeedback(feedbackForm, req.headers['user-agent']); + } catch (error) { + Logger.log(error); + throw new InternalServerErrorException(); + } + } + @Post('send') @Groups('emailWriter') @ApiOperation({ title: 'Send email.' }) diff --git a/src/email/email.service.ts b/src/email/email.service.ts index 179f9a03f1088768fa5276d9c1e1eab1d89252ff..525dc1dfbf0e2be381295bc5159ea0b990724f64 100644 --- a/src/email/email.service.ts +++ b/src/email/email.service.ts @@ -1,9 +1,11 @@ import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common'; import * as amqp from 'amqplib'; -import { ContactForm, Email, EmailWithoutFrom } from './email'; +import { ContactForm, Email, EmailWithoutFrom, FeedbackForm } from './email'; import { ConfigService } from '../configuration/config.service'; import { buildContactAdminEmail, buildContactUserEmail } from '../email-templates/contact'; import moment = require('moment'); +import { buildFeedbackEmail } from 'email-templates/feedback'; +import * as useragent from 'useragent'; @Injectable() export class EmailService { @@ -16,7 +18,6 @@ export class EmailService { async sendContactEmails(contactForm: ContactForm) { Logger.log('[-] sendContactEmails method'); - // contactForm.text = contactForm.text.replace(/(?:\r\n|\r|\n)/g, '<br />'); const adminEmailBody = buildContactAdminEmail({ subject: contactForm.subject, message: contactForm.text, @@ -51,13 +52,43 @@ export class EmailService { return; } + async sendFeedback(feedbackForm: FeedbackForm, userAgent) { + Logger.log('[-] sendFeedback method'); + + const userAgentParsed = useragent.parse(userAgent); + let userAgentString = ''; + + if (userAgentParsed) { + userAgentString = `Family: ${userAgentParsed.family},`; + userAgentString += `Version:${userAgentParsed.major}.${userAgentParsed.minor}.${userAgentParsed.patch},`; + userAgentString += `Source: ${userAgentParsed.source}`; + } + + const feedbackEmailBody = buildFeedbackEmail({ + url: feedbackForm.url, + userAgent: userAgentString, + version: feedbackForm.version, + message: feedbackForm.message, + datetime: moment().format('DD/MM/YYYY à HH:mm'), + imageHost: this.config.imageHost, + }); + const feedbackEmail = new EmailWithoutFrom(); + feedbackEmail.to = [this.config.plateformDataEmail]; + feedbackEmail.subject = 'Feedback'; + feedbackEmail.html = feedbackEmailBody; + + await this.send(feedbackEmail); + + return; + } + async send(emailInfo: EmailWithoutFrom) { let conn, ch; // tslint:disable-next-line:max-line-length const rabbitmqUrl = `amqp://${this.config.rabbitMQ.user}:${this.config.rabbitMQ.password}@${this.config.rabbitMQ.host}:${this.config.rabbitMQ.port}`; const mailerQueue = this.config.mailerQueue; - let email = new Email(); + let email = new Email(); email.from = this.config.plateformDataEmail; email = Object.assign(email, emailInfo); diff --git a/src/email/email.ts b/src/email/email.ts index 64f08f70f7bb6cd63f88ea530dc3068d1429be41..48387ecf6cd4153a14343336550d837b945abc7a 100644 --- a/src/email/email.ts +++ b/src/email/email.ts @@ -79,4 +79,18 @@ export class EmailWithoutFrom { @IsDefined() html: string; +} + +export class FeedbackForm { + @ApiModelProperty() + @IsDefined() + url: string; + + @ApiModelProperty() + @IsDefined() + version: string; + + @ApiModelProperty() + @IsDefined() + message: string; } \ No newline at end of file diff --git a/src/guards/groups.guards.ts b/src/guards/groups.guards.ts index 7028262312a50b04a08a6ac5ac9abf607f4d8a64..a56a946aab99e0f6c36cca4d3094e0d7de98e844 100644 --- a/src/guards/groups.guards.ts +++ b/src/guards/groups.guards.ts @@ -10,11 +10,18 @@ export class GroupsGuard implements CanActivate { Logger.log('[+] GroupMiddleware'); // We get the groups specified on the endpoint with the @Groups annotation // We need then to get the real group names associated to those keys from the config file - const groups = this.reflector.get<string[]>('groups', context.getHandler()).map((e) => { + let groups = this.reflector.get<string[]>('groups', context.getHandler()); + + // if no groups specified then access is always ok + if (!groups) { + return true; + } + + groups = groups.map((e) => { return this.configService.config.groupNames[e]; }); - // if no groups specified then access is always ok + // if the spcified group is not found if (!groups) { return true; } diff --git a/swagger-spec.json b/swagger-spec.json index 76c450a6a3dac630515836fd316d95553ad721af..c3eeaa52b35ab17fe14e80cb262345f2971b9a59 100644 --- a/swagger-spec.json +++ b/swagger-spec.json @@ -1 +1 @@ -{"swagger":"2.0","info":{"description":"Service providing the method to send emails.","version":"0.1","title":"Email service API"},"basePath":"/","tags":[{"name":"email","description":""}],"schemes":["http"],"paths":{"/email/contact":{"post":{"summary":"Send email to admin (emails defined as var env of the project, see docker-compose.yml file) and recap email to user email.","parameters":[{"name":"ContactForm","required":true,"in":"body","schema":{"$ref":"#/definitions/ContactForm"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Missing fields"},"500":{"description":"Internal error, this is probably a rabbitMQ related error (unreachable service...)"}},"tags":["email"],"produces":["application/json"],"consumes":["application/json"]}},"/email/send":{"post":{"summary":"Send email.","parameters":[{"name":"EmailWithoutFrom","required":true,"in":"body","schema":{"$ref":"#/definitions/EmailWithoutFrom"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Missing fields"},"500":{"description":"Internal error, this is probably a rabbitMQ related error (unreachable service...)"}},"tags":["email"],"produces":["application/json"],"consumes":["application/json"]}}},"definitions":{"ContactForm":{"type":"object","properties":{"email":{"type":"string"},"subject":{"type":"string"},"firstname":{"type":"string"},"lastname":{"type":"string"},"text":{"type":"string"}},"required":["email","subject","firstname","lastname","text"]},"EmailWithoutFrom":{"type":"object","properties":{"to":{"type":"array","items":{"type":"string"}},"replyTo":{"type":"string"},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"html":{"type":"string"}},"required":["to","subject","html"]}}} \ No newline at end of file +{"swagger":"2.0","info":{"description":"Service providing the method to send emails.","version":"0.1","title":"Email service API"},"basePath":"/","tags":[{"name":"email","description":""}],"schemes":["http"],"paths":{"/email/contact":{"post":{"summary":"Send email to admin (emails defined as var env of the project, see docker-compose.yml file) and recap email to user email.","parameters":[{"name":"ContactForm","required":true,"in":"body","schema":{"$ref":"#/definitions/ContactForm"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Missing fields"},"500":{"description":"Internal error, this is probably a rabbitMQ related error (unreachable service...)"}},"tags":["email"],"produces":["application/json"],"consumes":["application/json"]}},"/email/feedback":{"post":{"summary":"Send email to admin with the user feedback","parameters":[{"name":"FeedbackForm","required":true,"in":"body","schema":{"$ref":"#/definitions/FeedbackForm"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Missing fields"},"500":{"description":"Internal error, this is probably a rabbitMQ related error (unreachable service...)"}},"tags":["email"],"produces":["application/json"],"consumes":["application/json"]}},"/email/send":{"post":{"summary":"Send email.","parameters":[{"name":"EmailWithoutFrom","required":true,"in":"body","schema":{"$ref":"#/definitions/EmailWithoutFrom"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Missing fields"},"500":{"description":"Internal error, this is probably a rabbitMQ related error (unreachable service...)"}},"tags":["email"],"produces":["application/json"],"consumes":["application/json"]}}},"definitions":{"ContactForm":{"type":"object","properties":{"email":{"type":"string"},"subject":{"type":"string"},"firstname":{"type":"string"},"lastname":{"type":"string"},"text":{"type":"string"}},"required":["email","subject","firstname","lastname","text"]},"FeedbackForm":{"type":"object","properties":{"url":{"type":"string"},"version":{"type":"string"},"message":{"type":"string"}},"required":["url","version","message"]},"EmailWithoutFrom":{"type":"object","properties":{"to":{"type":"array","items":{"type":"string"}},"replyTo":{"type":"string"},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"html":{"type":"string"}},"required":["to","subject","html"]}}} \ No newline at end of file