diff options
| author | Gavin Mogan <git@gavinmogan.com> | 2025-04-23 17:27:55 -0700 | 
|---|---|---|
| committer | Gavin Mogan <git@gavinmogan.com> | 2025-04-23 17:43:37 -0700 | 
| commit | a8a17443c2d070d2d23920ffff7e4a43c905698c (patch) | |
| tree | cddb37a6af4a65f774863e51be941cd291af5683 | |
| parent | aace2c7e6ccb6e74df83faac74c427d43bfaf79b (diff) | |
Refactor for everywhere to use sendEmailFromTemplate everywhere
* Created a singleton to house handlebars so req doesn't need to be
  passed everywhere (should make unit testing easier later)
* Subjectline for sendgrid and nodemailer are both always prefixed in
  sendEmail()
* removed prefix subjectline from all other email places
* added a couple if (!event) { return 404 } to help make typescript
  happy
* some minor eslint auto fixes (looks like let => const where it can)
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 11 | ||||
| -rwxr-xr-x | src/app.ts | 68 | ||||
| -rw-r--r-- | src/lib/email.ts | 101 | ||||
| -rw-r--r-- | src/lib/handlebars.ts | 97 | ||||
| -rwxr-xr-x | src/routes.js | 112 | ||||
| -rw-r--r-- | src/routes/event.ts | 51 | ||||
| -rw-r--r-- | src/routes/group.ts | 6 | ||||
| -rw-r--r-- | src/routes/magicLink.ts | 6 | 
9 files changed, 188 insertions, 265 deletions
diff --git a/package.json b/package.json index 26d1d00..0287e77 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@      "author": "",      "license": "GPL-3.0-or-later",      "dependencies": { +        "@sendgrid/helpers": "^8.0.0",          "@sendgrid/mail": "^6.5.5",          "@types/cookie-parser": "^1.4.7",          "activitypub-types": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5139a4a..0ac81b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers:    .:      dependencies: +      '@sendgrid/helpers': +        specifier: ^8.0.0 +        version: 8.0.0        '@sendgrid/mail':          specifier: ^6.5.5          version: 6.5.5 @@ -370,6 +373,10 @@ packages:      resolution: {integrity: sha512-uRFEanalfss5hDsuzVXZ1wm7i7eEXHh1py80piOXjobiQ+MxmtR19EU+gDSXZ+uMcEehBGhxnb7QDNN0q65qig==}      engines: {node: '>= 6.0.0'} +  '@sendgrid/helpers@8.0.0': +    resolution: {integrity: sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==} +    engines: {node: '>= 12.0.0'} +    '@sendgrid/mail@6.5.5':      resolution: {integrity: sha512-DSu8oTPI0BJFH60jMOG9gM+oeNMoRALFmdAYg2PIXpL+Zbxd7L2GzQZtmf1jLy/8UBImkbB3D74TjiOBiLRK1w==}      engines: {node: '>=6.0.0'} @@ -2661,6 +2668,10 @@ snapshots:        chalk: 2.4.2        deepmerge: 4.3.1 +  '@sendgrid/helpers@8.0.0': +    dependencies: +      deepmerge: 4.3.1 +    '@sendgrid/mail@6.5.5':      dependencies:        '@sendgrid/client': 6.5.5 @@ -1,5 +1,4 @@  import express from "express"; -import hbs from "express-handlebars";  import cookieParser from "cookie-parser";  import routes from "./routes.js"; @@ -15,77 +14,16 @@ import {      activityPubContentType,      alternateActivityPubContentType,  } from "./lib/activitypub.js"; -import getConfig from "./lib/config.js"; +import { HandlebarsSingleton } from "./lib/handlebars.js";  const app = express(); -const config = getConfig();  initEmailService().then((sendEmails) => (app.locals.sendEmails = sendEmails));  // View engine // -const hbsInstance = hbs.create({ -    defaultLayout: "main", -    partialsDir: ["views/partials/"], -    layoutsDir: "views/layouts/", -    runtimeOptions: { -        data: { -            domain: config.general.domain, -            contactEmail: config.general.email, -            siteName: config.general.site_name, -            mailService: config.general.mail_service, -            siteLogo: config.general.email_logo_url, -            isFederated: config.general.is_federated || true, -        }, -    }, -    helpers: { -        plural: function (number: number, text: string) { -            var singular = number === 1; -            // If no text parameter was given, just return a conditional s. -            if (typeof text !== "string") return singular ? "" : "s"; -            // Split with regex into group1/group2 or group1(group3) -            var match = text.match(/^([^()\/]+)(?:\/(.+))?(?:\((\w+)\))?/); -            // If no match, just append a conditional s. -            if (!match) return text + (singular ? "" : "s"); -            // We have a good match, so fire away -            return ( -                (singular && match[1]) || // Singular case -                match[2] || // Plural case: 'bagel/bagels' --> bagels -                match[1] + (match[3] || "s") -            ); // Plural case: 'bagel(s)' or 'bagel' --> bagels -        }, -        json: function (context: any) { -            return JSON.stringify(context); -        }, -    }, -}); -app.locals.renderEmail = async function renderEmail( -    template: string, -    data: object -) { -    const [html, text] = await Promise.all([ -        hbsInstance.renderView( -            `./views/emails/${template}Html.handlebars`, -            { -                cache: true, -                layout: "email.handlebars", -                ...data, -            } -        ), -        hbsInstance.renderView( -            `./views/emails/${template}Text.handlebars`, -            { -                cache: true, -                layout: "email.handlebars", -                ...data, -            } -        ), -    ]); -    return { html, text } -} - -app.engine("handlebars", hbsInstance.engine); +app.engine("handlebars", HandlebarsSingleton.instance.engine);  app.set("view engine", "handlebars"); -app.set("hbsInstance", hbsInstance); +app.set("hbsInstance", HandlebarsSingleton.instance);  // Static files //  app.use(express.static("public")); diff --git a/src/lib/email.ts b/src/lib/email.ts index e7243aa..57f69f5 100644 --- a/src/lib/email.ts +++ b/src/lib/email.ts @@ -1,13 +1,15 @@  import sgMail from "@sendgrid/mail"; +import sgHelpers from "@sendgrid/helpers"; +  import nodemailer, { Transporter } from "nodemailer";  import { getConfig } from "./config.js";  import SMTPTransport from "nodemailer/lib/smtp-transport/index.js";  import { exitWithError } from "./process.js"; -import { renderTemplate } from "./handlebars.js"; -import { ExpressHandlebars } from "express-handlebars"; +import { HandlebarsSingleton } from "./handlebars.js"; +  const config = getConfig(); -type EmailTemplate = +type EmailTemplateName =      | "addEventAttendee"      | "addEventComment"      | "createEvent" @@ -16,6 +18,7 @@ type EmailTemplate =      | "deleteEvent"      | "editEvent"      | "eventGroupUpdated" +    | "removeEventAttendee"      | "subscribed"      | "unattendEvent"; @@ -89,30 +92,6 @@ export const initEmailService = async (): Promise<boolean> => {      }  }; -export const sendTemplatedEmail = async ( -    hbs: ExpressHandlebars, -    to: string, -    bcc: string, -    subject: string, -    template: string, -    data: object, -): Promise<boolean> => { -    const [html, text] = await Promise.all([ -        hbs.renderView(`./views/emails/${template}Html.handlebars`, { -            cache: true, -            layout: "email.handlebars", -            ...data, -        }), -        hbs.renderView(`./views/emails/${template}Text.handlebars`, { -            cache: true, -            layout: "email.handlebars", -            ...data, -        }), -    ]); - -    return await sendEmail(to, bcc, subject, text, html); -}; -  export const sendEmail = async (      to: string | string[],      bcc: string | string[] | undefined, @@ -132,11 +111,11 @@ export const sendEmail = async (                      html,                  });                  return true; -            } catch (e: Error) { -                if (e.response) { -                    console.error(e.response.body); +            } catch (e: unknown | sgHelpers.classes.ResponseError) { +                if (e instanceof sgHelpers.classes.ResponseError) { +                    console.error('sendgrid error', e.response.body);                  } else { -                    console.error(e); +                    console.error('sendgrid error', e);                  }                  return false;              } @@ -164,15 +143,10 @@ export const sendEmail = async (                          nodemailer.createTransport(nodemailerConfig);                  }                  await nodemailerTransporter.sendMail({ -                    envelope: { -                        from: config.general.email, -                        to, -                        bcc, -                    },                      from: config.general.email,                      to,                      bcc, -                    subject, +                    subject: `${config.general.site_name}: ${subject}`,                      text,                      html,                  }); @@ -187,25 +161,42 @@ export const sendEmail = async (  };  export const sendEmailFromTemplate = async ( -    to: string, -    bcc: string, +    to: string | string[], +    bcc: string | string[] | undefined,      subject: string, -    template: EmailTemplate, -    templateData: Record<string, unknown>, -    req: Request, +    templateName: EmailTemplateName, +    templateData: object,  ): Promise<boolean> => { -    const html = await renderTemplate(req, `${template}/${template}Html`, { -        siteName: config.general.site_name, -        siteLogo: config.general.email_logo_url, -        domain: config.general.domain, -        cache: true, -        layout: "email.handlebars", -        ...templateData, -    }); -    const text = await renderTemplate( -        req, -        `${template}/${template}Text`, -        templateData, -    ); +    const [html, text] = await Promise.all([ +        HandlebarsSingleton.instance.renderView( +            `./views/emails/${templateName}/${templateName}Html.handlebars`, +            { +                domain: config.general.domain, +                contactEmail: config.general.email, +                siteName: config.general.site_name, +                mailService: config.general.mail_service, +                siteLogo: config.general.email_logo_url, +                isFederated: config.general.is_federated || true, +                cache: true, +                layout: "email.handlebars", +                ...templateData, +            } +        ), +        HandlebarsSingleton.instance.renderView( +            `./views/emails/${templateName}/${templateName}Text.handlebars`, +            { +                domain: config.general.domain, +                contactEmail: config.general.email, +                siteName: config.general.site_name, +                mailService: config.general.mail_service, +                siteLogo: config.general.email_logo_url, +                isFederated: config.general.is_federated || true, +                cache: true, +                layout: "email.handlebars", +                ...templateData, +            } +        ), +    ]); +      return await sendEmail(to, bcc, subject, text, html);  }; diff --git a/src/lib/handlebars.ts b/src/lib/handlebars.ts index 42f8010..6d4f796 100644 --- a/src/lib/handlebars.ts +++ b/src/lib/handlebars.ts @@ -1,50 +1,55 @@ -import { Request } from "express"; -import { ExpressHandlebars } from "express-handlebars"; +import hbs, { ExpressHandlebars } from "express-handlebars"; +import { RenderViewOptions } from "express-handlebars/types/index.js"; -export const renderTemplate = async ( -    req: Request, -    templateName: string, -    data: Record<string, unknown>, -): Promise<string> => { -    return new Promise<string>((resolve, reject) => { -        req.app -            .get("hbsInstance") -            .renderView( -                `./views/emails/${templateName}.handlebars`, -                data, -                (err: any, html: string) => { -                    if (err) { -                        console.error(err); -                        reject(err); -                    } -                    resolve(html); +export class HandlebarsSingleton { +    static #instance: HandlebarsSingleton; +    hbsInstance: hbs.ExpressHandlebars; + +    private constructor() {  +        this.hbsInstance = hbs.create({ +            defaultLayout: "main", +            partialsDir: ["views/partials/"], +            layoutsDir: "views/layouts/", +            helpers: { +                plural: function (number: number, text: string) { +                    const singular = number === 1; +                    // If no text parameter was given, just return a conditional s. +                    if (typeof text !== "string") return singular ? "" : "s"; +                    // Split with regex into group1/group2 or group1(group3) +                    const match = text.match(/^([^()\/]+)(?:\/(.+))?(?:\((\w+)\))?/); +                    // If no match, just append a conditional s. +                    if (!match) return text + (singular ? "" : "s"); +                    // We have a good match, so fire away +                    return ( +                        (singular && match[1]) || // Singular case +                        match[2] || // Plural case: 'bagel/bagels' --> bagels +                        match[1] + (match[3] || "s") +                    ); // Plural case: 'bagel(s)' or 'bagel' --> bagels +                }, +                json: function (context: object) { +                    return JSON.stringify(context);                  }, -            ); -    }); -}; +            }, +        }); +    } + +    public static get instance(): HandlebarsSingleton { +        if (!HandlebarsSingleton.#instance) { +            HandlebarsSingleton.#instance = new HandlebarsSingleton(); +        } + +        return HandlebarsSingleton.#instance; +    } +     +    public get engine(): ExpressHandlebars["engine"] { +        return this.hbsInstance.engine; +    } -export const renderEmail = async ( -    hbsInstance: ExpressHandlebars, -    templateName: string, -    data: Record<string, unknown>, -): Promise<{ html: string, text: string }> => { -    const [html, text] = await Promise.all([ -        hbsInstance.renderView( -            `./views/emails/${templateName}Html.handlebars`, -            { -                cache: true, -                layout: "email.handlebars", -                ...data, -            } -        ), -        hbsInstance.renderView( -            `./views/emails/${templateName}Text.handlebars`, -            { -                cache: true, -                layout: "email.handlebars", -                ...data, -            } -        ), -    ]); -    return { html, text } +    /** +     * Finally, any singleton can define some business logic, which can be +     * executed on its instance. +     */ +    public renderView(viewPath: string, options: RenderViewOptions): Promise<string> { +        return this.hbsInstance.renderView(viewPath, options); +    }  } diff --git a/src/routes.js b/src/routes.js index f609f94..d5b5877 100755 --- a/src/routes.js +++ b/src/routes.js @@ -9,8 +9,6 @@ import crypto from "crypto";  import request from "request";  import niceware from "niceware";  import ical from "ical"; -import sgMail from "@sendgrid/mail"; -import nodemailer from "nodemailer";  import fileUpload from "express-fileupload";  import Jimp from "jimp";  import schedule from "node-schedule"; @@ -19,20 +17,15 @@ import {      broadcastDeleteMessage,      processInbox,  } from "./activitypub.js"; -import { renderEmail } from "./lib/handlebars.js";  import Event from "./models/Event.js";  import EventGroup from "./models/EventGroup.js";  import path from "path";  import { activityPubContentType } from "./lib/activitypub.js";  import { hashString } from "./util/generator.js"; -import { initEmailService, sendEmail } from "./lib/email.js"; +import { initEmailService, sendEmailFromTemplate } from "./lib/email.js";  const config = getConfig();  const domain = config.general.domain; -const contactEmail = config.general.email; -const siteName = config.general.site_name; -const mailService = config.general.mail_service; -const siteLogo = config.general.email_logo_url;  const isFederated = config.general.is_federated || true;  // This alphabet (used to generate all event, group, etc. IDs) is missing '-' @@ -329,14 +322,14 @@ router.post("/deleteevent/:eventID/:editToken", (req, res) => {                                              "Sending emails to: " +                                                  attendeeEmails,                                          ); -                                        renderEmail( -                                            req.app.get("hbsInstance"), -                                            "deleteEvent/deleteEvent", +                                        sendEmailFromTemplate( +                                            attendeeEmails,  +                                            '', +                                            `${event?.name} was deleted`, +                                            "deleteEvent",                                              {                                                  eventName: event?.name,                                              }, -                                        ).then( -                                            ({ html, text }) => sendEmail(attendeeEmails, '', `${siteName}: ${event?.name} was deleted`, text, html)                                          ).catch((e) => {                                              console.error('error sending attendy email', e.toString());                                              res.status(500).end(); @@ -634,6 +627,10 @@ router.post("/attendevent/:eventID", async (req, res) => {          },      )          .then((event) => { +            if (!event) { +                return res.sendStatus(404); +            } +              addToLog(                  "addEventAttendee",                  "success", @@ -641,9 +638,11 @@ router.post("/attendevent/:eventID", async (req, res) => {              );              if (sendEmails) {                  if (req.body.attendeeEmail) {           -                    renderEmail( -                        req.app.get("hbsInstance"), -                        "addEventAttendee/addEventAttendee", +                    sendEmailFromTemplate( +                        req.body.attendeeEmail, +                        '', +                        `You're RSVPed to ${event.name}`, +                        "addEventAttendee",                          {                              eventID: req.params.eventID,                              removalPassword: req.body.removalPassword, @@ -651,8 +650,6 @@ router.post("/attendevent/:eventID", async (req, res) => {                                  req.body.removalPassword,                              ),                          }, -                    ).then( -                        ({ html, text }) => sendEmail(req.body.attendeeEmail, '', `${siteName}: You're RSVPed to ${event.name}`, text, html)                      ).catch((e) => {                          console.error('error sending addEventAttendee email', e.toString());                          res.status(500).end(); @@ -686,11 +683,14 @@ router.get("/oneclickunattendevent/:eventID/:attendeeID", (req, res) => {      ) {          return res.sendStatus(200);      } -    Event.updateOne( +    Event.findOneAndUpdate(          { id: req.params.eventID },          { $pull: { attendees: { _id: req.params.attendeeID } } },      ) -        .then((response) => { +        .then((event) => { +            if (!event) { +                return res.sendStatus(404); +            }              addToLog(                  "oneClickUnattend",                  "success", @@ -699,14 +699,14 @@ router.get("/oneclickunattendevent/:eventID/:attendeeID", (req, res) => {              if (sendEmails) {                  // currently this is never called because we don't have the email address                  if (req.body.attendeeEmail) { -                    renderEmail( -                        req.app.get("hbsInstance"), -                        "removeEventAttendee/removeEventAttendee", +                    sendEmailFromTemplate( +                        req.body.attendeeEmail, +                        '',  +                        `You have been removed from an event`, +                        "removeEventAttendee",                          { -                            eventName: req.params.eventName, +                            eventName: event.name,                          }, -                    ).then( -                        ({ html, text }) => sendEmail(req.body.attendeeEmail, '', `${siteName}: You have been removed from an event`, text, html)                      ).catch((e) => {                          console.error('error sending removeEventAttendeeHtml email', e.toString());                          res.status(500).end(); @@ -732,11 +732,14 @@ router.get("/oneclickunattendevent/:eventID/:attendeeID", (req, res) => {  });  router.post("/removeattendee/:eventID/:attendeeID", (req, res) => { -    Event.updateOne( +    Event.findOneAndUpdate(          { id: req.params.eventID },          { $pull: { attendees: { _id: req.params.attendeeID } } },      ) -        .then((response) => { +        .then((event) => { +            if (!event) { +                return res.sendStatus(404); +            }              addToLog(                  "removeEventAttendee",                  "success", @@ -745,14 +748,14 @@ router.post("/removeattendee/:eventID/:attendeeID", (req, res) => {              if (sendEmails) {                  // currently this is never called because we don't have the email address                  if (req.body.attendeeEmail) { -                    renderEmail( -                        req.app.get("hbsInstance"), -                        "removeEventAttendee/removeEventAttendee", +                    sendEmailFromTemplate( +                        req.body.attendeeEmail,  +                        '',  +                        `You have been removed from an event`,  +                        "removeEventAttendee",                          { -                            eventName: req.params.eventName, +                            eventName: event.name,                          }, -                    ).then( -                        ({ html, text }) => sendEmail(req.body.attendeeEmail, '', `${siteName}: You have been removed from an event`, text, html)                      ).catch((e) => {                          console.error('error sending removeEventAttendeeHtml email', e.toString());                          res.status(500).end();                   @@ -798,16 +801,16 @@ router.post("/subscribe/:eventGroupID", (req, res) => {              eventGroup.subscribers.push(subscriber);              eventGroup.save();              if (sendEmails) { -                renderEmail( -                    req.app.get("hbsInstance"), -                    "subscribed/subscribed", +                sendEmailFromTemplate( +                    subscriber.email,  +                    '', +                    `You have subscribed to an event group`,  +                    "subscribed",                      {                          eventGroupName: eventGroup.name,                          eventGroupID: eventGroup.id,                          emailAddress: encodeURIComponent(subscriber.email),                      }, -                ).then( -                    ({ html, text }) => sendEmail(subscriber.email, '', `${siteName}: You have subscribed to an event group`, text, html)                  ).catch((e) => {                      console.error('error sending removeEventAttendeeHtml email', e.toString());                      res.status(500).end();                   @@ -875,7 +878,9 @@ router.post("/post/comment/:eventID", (req, res) => {              id: req.params.eventID,          },          function (err, event) { -            if (!event) return; +            if (!event) { +                return res.sendStatus(404); +            }              event.comments.push(newComment);              event                  .save() @@ -914,15 +919,15 @@ router.post("/post/comment/:eventID", (req, res) => {                                      console.log(                                          "Sending emails to: " + attendeeEmails,                                      ); -                                    renderEmail( -                                        req.app.get("hbsInstance"), -                                        "addEventComment/addEventComment", +                                    sendEmailFromTemplate( +                                        event?.creatorEmail || config.general.email, +                                        attendeeEmails, +                                        `New comment in ${event.name}`, +                                        "addEventComment",                                          {                                              eventID: req.params.eventID,                                              commentAuthor: req.body.commentAuthor,                                          }, -                                    ).then( -                                        ({ html, text }) => sendEmail(attendeeEmails, '', `${siteName}: New comment in ${event.name}`, text, html)                                      ).catch((e) => {                                          console.error('error sending removeEventAttendeeHtml email', e.toString());                                          res.status(500).end();                   @@ -967,7 +972,9 @@ router.post("/post/reply/:eventID/:commentID", (req, res) => {              id: req.params.eventID,          },          function (err, event) { -            if (!event) return; +            if (!event) { +                return res.sendStatus(404); +            }              var parentComment = event.comments.id(commentID);              parentComment.replies.push(newReply);              event @@ -999,6 +1006,9 @@ router.post("/post/reply/:eventID/:commentID", (req, res) => {                      if (sendEmails) {                          Event.findOne({ id: req.params.eventID }).then(                              (event) => { +                                if (!event) { +                                    return res.sendStatus(404); +                                }                                  const attendeeEmails = event.attendees                                      .filter(                                          (o) => @@ -1009,15 +1019,15 @@ router.post("/post/reply/:eventID/:commentID", (req, res) => {                                      console.log(                                          "Sending emails to: " + attendeeEmails,                                      ); -                                    renderEmail( -                                        req.app.get("hbsInstance"), -                                        "addEventComment/addEventComment", +                                    sendEmailFromTemplate( +                                        event?.creatorEmail || config.general.email, +                                        attendeeEmails, +                                        `New comment in ${event.name}`, +                                        "addEventComment",                                          {                                              eventID: req.params.eventID,                                              commentAuthor: req.body.replyAuthor,                                          }, -                                    ).then( -                                        ({ html, text }) => sendEmail(attendeeEmails, '', `${siteName}: New comment in ${event.name}`, text, html)                                      ).catch((e) => {                                          console.error('error sending removeEventAttendeeHtml email', e.toString());                                          res.status(500).end();                   diff --git a/src/routes/event.ts b/src/routes/event.ts index de5cb4c..f97adc9 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -80,8 +80,8 @@ router.post(              });          } -        let eventID = generateEventID(); -        let editToken = generateEditToken(); +        const eventID = generateEventID(); +        const editToken = generateEditToken();          let eventImageFilename;          let isPartOfEventGroup = false; @@ -125,7 +125,7 @@ router.post(          }          // generate RSA keypair for ActivityPub -        let { publicKey, privateKey } = generateRSAKeypair(); +        const { publicKey, privateKey } = generateRSAKeypair();          const event = new Event({              id: eventID, @@ -202,11 +202,7 @@ router.post(                      {                          eventID,                          editToken, -                        siteName: res.locals.config?.general.site_name, -                        siteLogo: res.locals.config?.general.email_logo_url, -                        domain: res.locals.config?.general.domain, -                    }, -                    req, +                    }                  );              }              // If the event was added to a group, send an email to any group @@ -237,17 +233,12 @@ router.post(                              `New event in ${eventGroup.name}`,                              "eventGroupUpdated",                              { -                                siteName: res.locals.config?.general.site_name, -                                siteLogo: -                                    res.locals.config?.general.email_logo_url, -                                domain: res.locals.config?.general.domain,                                  eventGroupName: eventGroup.name,                                  eventName: event.name,                                  eventID: event.id,                                  eventGroupID: eventGroup.id,                                  emailAddress: encodeURIComponent(emailAddress), -                            }, -                            req, +                            }                          );                      });                  } catch (err) { @@ -332,7 +323,7 @@ router.put(              }              // Token matches              // If there is a new image, upload that first -            let eventID = req.params.eventID; +            const eventID = req.params.eventID;              let eventImageFilename = event.image;              if (req.file?.buffer) {                  Jimp.read(req.file.buffer) @@ -452,7 +443,7 @@ router.put(                  "Event " + req.params.eventID + " edited",              );              // send update to ActivityPub subscribers -            let attendees = updatedEventObject.attendees?.filter((el) => el.id); +            const attendees = updatedEventObject.attendees?.filter((el) => el.id);              // broadcast an identical message to all followers, will show in home timeline              const guidObject = crypto.randomBytes(16).toString("hex");              const jsonObject = { @@ -495,21 +486,17 @@ router.put(              if (req.app.locals.sendEmails) {                  const attendeeEmails = event.attendees                      ?.filter((o) => o.status === "attending" && o.email) -                    .map((o) => o.email); +                    .map((o) => o.email!);                  if (attendeeEmails?.length) {                      sendEmailFromTemplate(                          config.general.email, -                        attendeeEmails.join(","), +                        attendeeEmails,                          `${event.name} was just edited`,                          "editEvent",                          {                              diffText,                              eventID: req.params.eventID, -                            siteName: res.locals.config?.general.site_name, -                            siteLogo: res.locals.config?.general.email_logo_url, -                            domain: res.locals.config?.general.domain,                          }, -                        req,                      );                  }              } @@ -550,12 +537,12 @@ router.post(              });          } -        let eventID = generateEventID(); -        let editToken = generateEditToken(); +        const eventID = generateEventID(); +        const editToken = generateEditToken(); -        let iCalObject = ical.parseICS(req.file.buffer.toString("utf8")); +        const iCalObject = ical.parseICS(req.file.buffer.toString("utf8")); -        let importedEventData = iCalObject[Object.keys(iCalObject)[0]]; +        const importedEventData = iCalObject[Object.keys(iCalObject)[0]];          let creatorEmail: string | undefined;          if (req.body.creatorEmail) { @@ -620,11 +607,7 @@ router.post(                      {                          eventID,                          editToken, -                        siteName: res.locals.config?.general.site_name, -                        siteLogo: res.locals.config?.general.email_logo_url, -                        domain: res.locals.config?.general.domain,                      }, -                    req,                  );              }              return res.json({ @@ -700,11 +683,7 @@ router.delete(                      "unattendEvent",                      {                          eventID: req.params.eventID, -                        siteName: res.locals.config?.general.site_name, -                        siteLogo: res.locals.config?.general.email_logo_url, -                        domain: res.locals.config?.general.domain,                      }, -                    req,                  );              }              res.sendStatus(200); @@ -752,11 +731,7 @@ router.get(                  "unattendEvent",                  {                      event, -                    siteName: res.locals.config?.general.site_name, -                    siteLogo: res.locals.config?.general.email_logo_url, -                    domain: res.locals.config?.general.domain,                  }, -                req,              );          }          return res.redirect(`/${req.params.eventID}?m=unattend`); diff --git a/src/routes/group.ts b/src/routes/group.ts index 9f4105c..c63413e 100644 --- a/src/routes/group.ts +++ b/src/routes/group.ts @@ -101,11 +101,7 @@ router.post(                      {                          eventGroupID: eventGroup.id,                          editToken: eventGroup.editToken, -                        siteName: res.locals.config?.general.site_name, -                        siteLogo: res.locals.config?.general.email_logo_url, -                        domain: res.locals.config?.general.domain,                      }, -                    req,                  );              } @@ -182,7 +178,7 @@ router.put(              }              // Token matches              // If there is a new image, upload that first -            let eventGroupID = req.params.eventGroupID; +            const eventGroupID = req.params.eventGroupID;              let eventGroupImageFilename = eventGroup.image;              if (req.file?.buffer) {                  Jimp.read(req.file.buffer) diff --git a/src/routes/magicLink.ts b/src/routes/magicLink.ts index b4afca6..8d0f147 100644 --- a/src/routes/magicLink.ts +++ b/src/routes/magicLink.ts @@ -54,12 +54,8 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => {          `Magic link to create an event`,          "createEventMagicLink",          { -            token, -            siteName: res.locals.config?.general.site_name, -            siteLogo: res.locals.config?.general.email_logo_url, -            domain: res.locals.config?.general.domain, +            token          }, -        req,      );      res.render("createEventMagicLink", {          ...frontendConfig(res),  | 
