From 23e49c6e6e63a518e704f82879a5fdcf268c51d8 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Tue, 8 Apr 2025 22:14:04 +0900 Subject: 1st stage, only language switch. Thank you, MomentQYC ( https://github.com/MomentQYC ). Your first attemt encourage me. --- src/routes/frontend.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 14bb779..96d7587 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -8,7 +8,7 @@ import { instanceDescription, instanceRules, } from "../lib/config.js"; -import { addToLog, exportICal } from "../helpers.js"; +import { addToLog, exportIcal } from "../helpers.js"; import Event from "../models/Event.js"; import EventGroup, { IEventGroup } from "../models/EventGroup.js"; import { @@ -546,7 +546,7 @@ router.get( const events = await Event.find({ eventGroup: eventGroup._id, }).sort("start"); - const string = exportICal(events, eventGroup.name); + const string = exportIcal(events, eventGroup.name); res.set("Content-Type", "text/calendar").send(string); } } catch (err) { @@ -568,7 +568,7 @@ router.get("/export/event/:eventID", async (req: Request, res: Response) => { }).populate("eventGroup"); if (event) { - const string = exportICal([event], event.name); + const string = exportIcal([event], event.name); res.set("Content-Type", "text/calendar").send(string); } } catch (err) { @@ -594,7 +594,7 @@ router.get( const events = await Event.find({ eventGroup: eventGroup._id, }).sort("start"); - const string = exportICal(events, eventGroup.name); + const string = exportIcal(events, eventGroup.name); res.set("Content-Type", "text/calendar").send(string); } } catch (err) { -- cgit v1.2.3 From f2ee19f15a78125a1dc2ba8b9c175dd9831e5700 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Thu, 20 Mar 2025 22:54:38 +0900 Subject: hidden attendees (? people) --- src/routes/frontend.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 96d7587..40b5393 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -19,6 +19,7 @@ import MagicLink from "../models/MagicLink.js"; import { getConfigMiddleware } from "../lib/middleware.js"; import { getMessage } from "../util/messages.js"; import { EventListEvent, bucketEventsByMonth } from "../lib/event.js"; +import i18next from "i18next"; const router = Router(); @@ -257,7 +258,7 @@ router.get("/:eventID", async (req: Request, res: Response) => { el.id = el._id; } if (el.number && el.number > 1) { - el.name = `${el.name} (${el.number} people)`; + el.name = `${el.name} ${i18next.t("frontend.elnumber", { count: el.number })}`; } return { ...el, -- cgit v1.2.3 From 2449234e28aab435ffe28d567ece8c651d45d2b3 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sat, 22 Mar 2025 21:03:25 +0900 Subject: DateTime format, and some translation fix --- src/routes/frontend.ts | 91 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 33 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 40b5393..6f9e00a 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -71,7 +71,7 @@ router.get("/new/:magicLinkToken", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "danger", - text: "This magic link is invalid or has expired. Please request a new one here.", + text: i18next.t("magiclink-invalid"), }, }); } @@ -92,6 +92,7 @@ router.get("/events", async (_: Request, res: Response) => { .lean() .sort("start"); const updatedEvents: EventListEvent[] = events.map((event) => { + moment.locale(i18next.language); const startMoment = moment.tz(event.start, event.timezone); const endMoment = moment.tz(event.end, event.timezone); const isSameDay = startMoment.isSame(endMoment, "day"); @@ -101,9 +102,9 @@ router.get("/events", async (_: Request, res: Response) => { name: event.name, location: event.location, displayDate: isSameDay - ? startMoment.format("D MMM YYYY") - : `${startMoment.format("D MMM YYYY")} - ${endMoment.format( - "D MMM YYYY", + ? startMoment.format("LL") + : `${startMoment.format("LL")} - ${endMoment.format( + "LL", )}`, eventHasConcluded: endMoment.isBefore(moment.tz(event.timezone)), eventGroup: event.eventGroup as any as IEventGroup, @@ -133,7 +134,7 @@ router.get("/events", async (_: Request, res: Response) => { }); res.render("publicEventList", { - title: "Public events", + title: i18next.t("frontend.publicevents"), upcomingEvents: upcomingEventsInMonthBuckets, pastEvents: pastEventsInMonthBuckets, eventGroups: updatedEventGroups, @@ -155,31 +156,57 @@ router.get("/:eventID", async (req: Request, res: Response) => { } const parsedLocation = event.location.replace(/\s+/g, "+"); let displayDate; + moment.locale(i18next.language); + const dateformat = i18next.t("frontend.dateformat"); + const timeformat = i18next.t('frontend.timeformat'); if (moment.tz(event.end, event.timezone).isSame(event.start, "day")) { // Happening during one day - displayDate = - moment - .tz(event.start, event.timezone) - .format( - 'dddd D MMMM YYYY [from] h:mm a', - ) + - moment - .tz(event.end, event.timezone) - .format( - ' [to] h:mm a [](z)[]', - ); + displayDate = i18next.t("frontend.displaydate-sameday", + { + startdate: + moment + .tz(event.start, event.timezone) + .format(dateformat), + starttime: + moment + .tz(event.start, event.timezone) + .format(timeformat), + endtime: + moment + .tz(event.end, event.timezone) + .format(timeformat), + timezone: + i18next.t('frontend.sameday.timezone', + { tz: + moment + .tz(event.end, event.timezone) + .format('(z)',) + } ) + }); } else { - displayDate = - moment - .tz(event.start, event.timezone) - .format( - 'dddd D MMMM YYYY [at] h:mm a', - ) + - moment - .tz(event.end, event.timezone) - .format( - ' [] dddd D MMMM YYYY [at] h:mm a [](z)[]', - ); + displayDate = i18next.t("frontend.displaydate-days", + { + startdate: + moment + .tz(event.start, event.timezone) + .format(dateformat), + starttime: + moment + .tz(event.start, event.timezone) + .format(timeformat), + enddate: + moment + .tz(event.end, event.timezone) + .format(dateformat), + endtime: + moment + .tz(event.end, event.timezone) + .format(timeformat), + timezone: + moment + .tz(event.end, event.timezone) + .format('(z)',) + }); } let eventStartISO = moment.tz(event.start, "Etc/UTC").toISOString(); let eventEndISO = moment.tz(event.end, "Etc/UTC").toISOString(); @@ -430,8 +457,8 @@ router.get("/group/:eventGroupID", async (req: Request, res: Response) => { .sort("start"); const updatedEvents: EventListEvent[] = events.map((event) => { - const startMoment = moment.tz(event.start, event.timezone); - const endMoment = moment.tz(event.end, event.timezone); + const startMoment = moment.tz(event.start, event.timezone).locale(i18next.language); + const endMoment = moment.tz(event.end, event.timezone).locale(i18next.language); const isSameDay = startMoment.isSame(endMoment, "day"); return { @@ -439,10 +466,8 @@ router.get("/group/:eventGroupID", async (req: Request, res: Response) => { name: event.name, location: event.location, displayDate: isSameDay - ? startMoment.format("D MMM YYYY") - : `${startMoment.format("D MMM YYYY")} - ${endMoment.format( - "D MMM YYYY", - )}`, + ? startMoment.format("LL") + : `${startMoment.format("LL")} - ${endMoment.format("LL")}`, eventHasConcluded: endMoment.isBefore( moment.tz(event.timezone), ), -- cgit v1.2.3 From 73e8b168c3ffc4e3ffe30e50dd3e46ed70d909d8 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sat, 22 Mar 2025 22:55:46 +0900 Subject: some fix, moment.locale setting --- src/routes/frontend.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 6f9e00a..44d3a76 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -50,7 +50,7 @@ router.get("/new", (req: Request, res: Response) => { return res.render("createEventMagicLink", frontendConfig(res)); } return res.render("newevent", { - title: "New event", + title: i18next.t("frontend.newevent"), ...frontendConfig(res), }); }); @@ -76,7 +76,7 @@ router.get("/new/:magicLinkToken", async (req: Request, res: Response) => { }); } res.render("newevent", { - title: "New event", + title: i18next.t("frontend.newevent"), ...frontendConfig(res), magicLinkToken: req.params.magicLinkToken, creatorEmail: magicLink.email, @@ -92,7 +92,6 @@ router.get("/events", async (_: Request, res: Response) => { .lean() .sort("start"); const updatedEvents: EventListEvent[] = events.map((event) => { - moment.locale(i18next.language); const startMoment = moment.tz(event.start, event.timezone); const endMoment = moment.tz(event.end, event.timezone); const isSameDay = startMoment.isSame(endMoment, "day"); @@ -156,7 +155,6 @@ router.get("/:eventID", async (req: Request, res: Response) => { } const parsedLocation = event.location.replace(/\s+/g, "+"); let displayDate; - moment.locale(i18next.language); const dateformat = i18next.t("frontend.dateformat"); const timeformat = i18next.t('frontend.timeformat'); if (moment.tz(event.end, event.timezone).isSame(event.start, "day")) { -- cgit v1.2.3 From 79d28b2a14cfab50b9a5a42e7d85ce7dedc7c180 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sun, 23 Mar 2025 14:06:00 +0900 Subject: MagicLink translation --- src/routes/magicLink.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/routes') diff --git a/src/routes/magicLink.ts b/src/routes/magicLink.ts index b4afca6..5eb424b 100644 --- a/src/routes/magicLink.ts +++ b/src/routes/magicLink.ts @@ -4,6 +4,7 @@ import { sendEmailFromTemplate } from "../lib/email.js"; import { generateMagicLinkToken } from "../util/generator.js"; import MagicLink from "../models/MagicLink.js"; import { getConfigMiddleware } from "../lib/middleware.js"; +import i18next from "i18next"; const router = Router(); @@ -16,7 +17,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "danger", - text: "Please provide an email address.", + text: i18next.t("ml.provideemail"), }, }); return; @@ -31,7 +32,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "success", - text: "Thanks! If this email address can create events, you should receive an email with a magic link.", + text: i18next.t("ml.thanks"), }, }); return; @@ -51,7 +52,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { sendEmailFromTemplate( email, "", - `Magic link to create an event`, + i18next.t("ml.mailsubject"), "createEventMagicLink", { token, @@ -65,7 +66,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "success", - text: "Thanks! If this email address can create events, you should receive an email with a magic link.", + text: i18next.t("ml.thanks"), }, }); }); -- cgit v1.2.3 From 61e2bfd5b1e7b601ca6e8d5f1f32b04b47697e7c Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Mon, 24 Mar 2025 21:36:57 +0900 Subject: some translation fix --- src/routes/frontend.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 44d3a76..387cd65 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -174,12 +174,9 @@ router.get("/:eventID", async (req: Request, res: Response) => { .tz(event.end, event.timezone) .format(timeformat), timezone: - i18next.t('frontend.sameday.timezone', - { tz: - moment - .tz(event.end, event.timezone) - .format('(z)',) - } ) + moment + .tz(event.end, event.timezone) + .format(' (z)',) }); } else { displayDate = i18next.t("frontend.displaydate-days", -- cgit v1.2.3 From b56f838718386e611af71a74b479a331b832da79 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Mon, 24 Mar 2025 21:50:25 +0900 Subject: mails translation --- src/routes/event.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src/routes') diff --git a/src/routes/event.ts b/src/routes/event.ts index de5cb4c..ca333c5 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -28,6 +28,8 @@ import ical from "ical"; import { markdownToSanitizedHTML } from "../util/markdown.js"; import { checkMagicLink, getConfigMiddleware } from "../lib/middleware.js"; import { getConfig } from "../lib/config.js"; +import i18next from "i18next"; +moment.locale(i18next.language); const config = getConfig(); @@ -409,33 +411,33 @@ router.put( : undefined, }; let diffText = - "

This event was just updated with new information.

    "; + "

    " + i18next.t("routes.event.difftext") + "

      "; let displayDate; if (event.name !== updatedEvent.name) { - diffText += `
    • the event name changed to ${updatedEvent.name}
    • `; + diffText += `
    • ` + i18next.t("routes.event.namechanged") + updatedEvent.name + `
    • `; } if (event.location !== updatedEvent.location) { - diffText += `
    • the location changed to ${updatedEvent.location}
    • `; + diffText += `
    • ` + i18next.t("routes.event.locationchanged") + updatedEvent.location + `
    • `; } if ( event.start.toISOString() !== updatedEvent.start.toISOString() ) { displayDate = moment .tz(updatedEvent.start, updatedEvent.timezone) - .format("dddd D MMMM YYYY h:mm a"); - diffText += `
    • the start time changed to ${displayDate}
    • `; + .format(i18next.t("common.datetimeformat")); + diffText += `
    • ` + i18next.t("routes.event.starttimechanged") + displayDate + `
    • `; } if (event.end.toISOString() !== updatedEvent.end.toISOString()) { displayDate = moment .tz(updatedEvent.end, updatedEvent.timezone) - .format("dddd D MMMM YYYY h:mm a"); - diffText += `
    • the end time changed to ${displayDate}
    • `; + .format(i18next.t("common.datetimeformat")); + diffText += `
    • ` + i18next.t("routes.event.endtimechanged") + displayDate + `
    • `; } if (event.timezone !== updatedEvent.timezone) { - diffText += `
    • the time zone changed to ${updatedEvent.timezone}
    • `; + diffText += `
    • ` + i18next.t("routes.event.timezonechanged") + updatedEvent.timezone + `
    • `; } if (event.description !== updatedEvent.description) { - diffText += `
    • the event description changed
    • `; + diffText += `
    • ` + i18next.t("routes.event.descriptionchanged") + `
    • `; } diffText += `
    `; const updatedEventObject = await Event.findOneAndUpdate( @@ -500,7 +502,7 @@ router.put( sendEmailFromTemplate( config.general.email, attendeeEmails.join(","), - `${event.name} was just edited`, + `${event.name} ` + i18next.t("routes.event.editedsubject"), "editEvent", { diffText, @@ -696,7 +698,7 @@ router.delete( await sendEmailFromTemplate( attendeeEmail, "", - "You have been removed from an event", + i18next.t("routes.removeeventattendeesubject"), "unattendEvent", { eventID: req.params.eventID, -- cgit v1.2.3 From 1fd6a7d2249fdf6ba5ca9ced6ea43348ebe0941d Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Wed, 2 Apr 2025 01:49:11 +0900 Subject: rename translation keys as filestructure --- src/routes/frontend.ts | 2 +- src/routes/magicLink.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 387cd65..16a44c8 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -71,7 +71,7 @@ router.get("/new/:magicLinkToken", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "danger", - text: i18next.t("magiclink-invalid"), + text: i18next.t("routes.magiclink-invalid"), }, }); } diff --git a/src/routes/magicLink.ts b/src/routes/magicLink.ts index 5eb424b..14312c1 100644 --- a/src/routes/magicLink.ts +++ b/src/routes/magicLink.ts @@ -17,7 +17,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "danger", - text: i18next.t("ml.provideemail"), + text: i18next.t("routes.magiclink.provideemail"), }, }); return; @@ -32,7 +32,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "success", - text: i18next.t("ml.thanks"), + text: i18next.t("routes.magiclink.thanks"), }, }); return; @@ -52,7 +52,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { sendEmailFromTemplate( email, "", - i18next.t("ml.mailsubject"), + i18next.t("routes.magiclink.mailsubject"), "createEventMagicLink", { token, @@ -66,7 +66,7 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { ...frontendConfig(res), message: { type: "success", - text: i18next.t("ml.thanks"), + text: i18next.t("routes.magiclink.thanks"), }, }); }); -- cgit v1.2.3 From aace2c7e6ccb6e74df83faac74c427d43bfaf79b Mon Sep 17 00:00:00 2001 From: Gavin Mogan Date: Wed, 23 Apr 2025 15:06:54 -0700 Subject: Fix ReferenceError: nodemailerTransporter is not defined Part of https://github.com/lowercasename/gathio/pull/200 was migrating more code to use the shared init email function, but all the local usages of nodemailerTransporter were missed --- src/routes/frontend.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 14bb779..1b95763 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -1,5 +1,4 @@ import { Router, Request, Response } from "express"; -import fs from "fs"; import moment from "moment-timezone"; import { marked } from "marked"; import { markdownToSanitizedHTML, renderPlain } from "../util/markdown.js"; @@ -25,7 +24,7 @@ const router = Router(); // Add config middleware to all routes router.use(getConfigMiddleware); -router.get("/", (_: Request, res: Response) => { +router.get("/", (_, res) => { if (res.locals.config?.general.show_public_event_list) { return res.redirect("/events"); } @@ -44,7 +43,7 @@ router.get("/about", (_: Request, res: Response) => { }); }); -router.get("/new", (req: Request, res: Response) => { +router.get("/new", (_: Request, res: Response) => { if (res.locals.config?.general.creator_email_addresses?.length) { return res.render("createEventMagicLink", frontendConfig(res)); } -- cgit v1.2.3 From a8a17443c2d070d2d23920ffff7e4a43c905698c Mon Sep 17 00:00:00 2001 From: Gavin Mogan Date: Wed, 23 Apr 2025 17:27:55 -0700 Subject: 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) --- src/routes/event.ts | 51 +++++++++++++------------------------------------ src/routes/group.ts | 6 +----- src/routes/magicLink.ts | 6 +----- 3 files changed, 15 insertions(+), 48 deletions(-) (limited to 'src/routes') 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), -- cgit v1.2.3 From 14041a319cace03cfc23c0a919ed81fb141f88ce Mon Sep 17 00:00:00 2001 From: Gavin Mogan Date: Fri, 25 Apr 2025 21:43:39 -0700 Subject: Refactor to have email service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move hbsInstance back to app * Add email and hbs to req so typescript 🎉🎉🎉 * Init Email and config once --- src/routes/event.ts | 146 +++++++++++++++++++++++------------------------- src/routes/group.ts | 22 ++++---- src/routes/magicLink.ts | 14 ++--- 3 files changed, 85 insertions(+), 97 deletions(-) (limited to 'src/routes') diff --git a/src/routes/event.ts b/src/routes/event.ts index f97adc9..ee45d96 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -22,14 +22,13 @@ import { updateActivityPubActor, updateActivityPubEvent, } from "../activitypub.js"; -import { sendEmailFromTemplate } from "../lib/email.js"; import crypto from "crypto"; import ical from "ical"; import { markdownToSanitizedHTML } from "../util/markdown.js"; import { checkMagicLink, getConfigMiddleware } from "../lib/middleware.js"; import { getConfig } from "../lib/config.js"; -const config = getConfig(); +const config = getConfig(); const storage = multer.memoryStorage(); // Accept only JPEG, GIF or PNG images, up to 10MB @@ -193,21 +192,20 @@ router.post( const savedEvent = await event.save(); addToLog("createEvent", "success", "Event " + eventID + "created"); // Send email with edit link - if (eventData.creatorEmail && req.app.locals.sendEmails) { - sendEmailFromTemplate( - eventData.creatorEmail, - "", - `${eventData.eventName}`, - "createEvent", - { + if (eventData.creatorEmail) { + req.emailService.sendEmailFromTemplate({ + to: eventData.creatorEmail, + subject: eventData.eventName, + templateName: "createEvent", + templateData: { eventID, editToken, } - ); + }); } // If the event was added to a group, send an email to any group // subscribers - if (event.eventGroup && req.app.locals.sendEmails) { + if (event.eventGroup) { try { const eventGroup = await EventGroup.findOne({ _id: event.eventGroup.toString(), @@ -227,19 +225,18 @@ router.post( [] as string[], ); subscribers?.forEach((emailAddress) => { - sendEmailFromTemplate( - emailAddress, - "", - `New event in ${eventGroup.name}`, - "eventGroupUpdated", - { + req.emailService.sendEmailFromTemplate({ + to: emailAddress, + subject: `New event in ${eventGroup.name}`, + templateName: "eventGroupUpdated", + templateData: { eventGroupName: eventGroup.name, eventName: event.name, eventID: event.id, eventGroupID: eventGroup.id, emailAddress: encodeURIComponent(emailAddress), } - ); + }); }); } catch (err) { console.error(err); @@ -247,7 +244,7 @@ router.post( "createEvent", "error", "Attempt to send event group emails failed with error: " + - err, + err, ); } } @@ -379,24 +376,24 @@ router.put( eventGroup: isPartOfEventGroup ? eventGroup?._id : null, activityPubActor: event.activityPubActor ? updateActivityPubActor( - JSON.parse(event.activityPubActor), - eventData.eventDescription, - eventData.eventName, - eventData.eventLocation, - eventImageFilename, - startUTC, - endUTC, - eventData.timezone, - ) + JSON.parse(event.activityPubActor), + eventData.eventDescription, + eventData.eventName, + eventData.eventLocation, + eventImageFilename, + startUTC, + endUTC, + eventData.timezone, + ) : undefined, activityPubEvent: event.activityPubEvent ? updateActivityPubEvent( - JSON.parse(event.activityPubEvent), - eventData.eventName, - startUTC, - endUTC, - eventData.timezone, - ) + JSON.parse(event.activityPubEvent), + eventData.eventName, + startUTC, + endUTC, + eventData.timezone, + ) : undefined, }; let diffText = @@ -483,22 +480,20 @@ router.put( } } // Send update to all attendees - if (req.app.locals.sendEmails) { - const attendeeEmails = event.attendees - ?.filter((o) => o.status === "attending" && o.email) - .map((o) => o.email!); - if (attendeeEmails?.length) { - sendEmailFromTemplate( - config.general.email, - attendeeEmails, - `${event.name} was just edited`, - "editEvent", - { - diffText, - eventID: req.params.eventID, - }, - ); - } + const attendeeEmails = event.attendees + ?.filter((o) => o.status === "attending" && o.email) + .map((o) => o.email!); + if (attendeeEmails?.length) { + req.emailService.sendEmailFromTemplate({ + to: config.general.email, + bcc: attendeeEmails, + subject: `${event.name} was just edited`, + templateName: "editEvent", + templateData: { + diffText, + eventID: req.params.eventID, + }, + }); } res.sendStatus(200); } catch (err) { @@ -507,9 +502,9 @@ router.put( "editEvent", "error", "Attempt to edit event " + - req.params.eventID + - " failed with error: " + - err, + req.params.eventID + + " failed with error: " + + err, ); return res.status(500).json({ errors: [ @@ -598,17 +593,16 @@ router.post( await event.save(); addToLog("createEvent", "success", `Event ${eventID} created`); // Send email with edit link - if (creatorEmail && req.app.locals.sendEmails) { - sendEmailFromTemplate( - creatorEmail, - "", - `${importedEventData.summary}`, - "createEvent", - { + if (creatorEmail) { + req.emailService.sendEmailFromTemplate({ + to: creatorEmail, + subject: importedEventData.summary || "", + templateName: "createEvent", + templateData: { eventID, editToken, }, - ); + }); } return res.json({ eventID: eventID, @@ -675,16 +669,15 @@ router.delete( "success", `Attendee removed self from event ${req.params.eventID}`, ); - if (attendeeEmail && req.app.locals.sendEmails) { - await sendEmailFromTemplate( - attendeeEmail, - "", - "You have been removed from an event", - "unattendEvent", - { + if (attendeeEmail) { + await req.emailService.sendEmailFromTemplate({ + to: attendeeEmail, + subject: "You have been removed from an event", + templateName: "unattendEvent", + templateData: { eventID: req.params.eventID, }, - ); + }); } res.sendStatus(200); } catch (e) { @@ -723,16 +716,15 @@ router.get( ); await event.save(); // Send email to the attendee - if (req.app.locals.sendEmails && attendee.email) { - sendEmailFromTemplate( - attendee.email, - "", - `You have been removed from ${event.name}`, - "unattendEvent", - { + if (attendee.email) { + req.emailService.sendEmailFromTemplate({ + to: attendee.email, + subject: `You have been removed from ${event.name}`, + templateName: "unattendEvent", + templateData: { event, }, - ); + }); } return res.redirect(`/${req.params.eventID}?m=unattend`); }, diff --git a/src/routes/group.ts b/src/routes/group.ts index c63413e..cc53976 100644 --- a/src/routes/group.ts +++ b/src/routes/group.ts @@ -5,7 +5,6 @@ import { validateGroupData } from "../util/validation.js"; import Jimp from "jimp"; import { addToLog } from "../helpers.js"; import EventGroup from "../models/EventGroup.js"; -import { sendEmailFromTemplate } from "../lib/email.js"; import { marked } from "marked"; import { renderPlain } from "../util/markdown.js"; import { checkMagicLink, getConfigMiddleware } from "../lib/middleware.js"; @@ -92,17 +91,16 @@ router.post( ); // Send email with edit link - if (groupData.creatorEmail && req.app.locals.sendEmails) { - sendEmailFromTemplate( - groupData.creatorEmail, - "", - `${eventGroup.name}`, - "createEventGroup", - { + if (groupData.creatorEmail) { + req.emailService.sendEmailFromTemplate({ + to: groupData.creatorEmail, + subject: eventGroup.name, + templateName: "createEventGroup", + templateData: { eventGroupID: eventGroup.id, editToken: eventGroup.editToken, }, - ); + }); } res.status(200).json({ @@ -224,9 +222,9 @@ router.put( "editEventGroup", "error", "Attempt to edit event group " + - req.params.eventGroupID + - " failed with error: " + - err, + req.params.eventGroupID + + " failed with error: " + + err, ); return res.status(500).json({ errors: [ diff --git a/src/routes/magicLink.ts b/src/routes/magicLink.ts index 8d0f147..e0a6310 100644 --- a/src/routes/magicLink.ts +++ b/src/routes/magicLink.ts @@ -1,6 +1,5 @@ import { Router, Request, Response } from "express"; import { frontendConfig } from "../lib/config.js"; -import { sendEmailFromTemplate } from "../lib/email.js"; import { generateMagicLinkToken } from "../util/generator.js"; import MagicLink from "../models/MagicLink.js"; import { getConfigMiddleware } from "../lib/middleware.js"; @@ -48,15 +47,14 @@ router.post("/magic-link/event/create", async (req: Request, res: Response) => { // Take this opportunity to delete any expired magic links await MagicLink.deleteMany({ expiryTime: { $lt: new Date() } }); - sendEmailFromTemplate( - email, - "", - `Magic link to create an event`, - "createEventMagicLink", - { + req.emailService.sendEmailFromTemplate({ + to: email, + subject: "Magic link to create an event", + templateName: "createEventMagicLink", + templateData: { token }, - ); + }); res.render("createEventMagicLink", { ...frontendConfig(res), message: { -- cgit v1.2.3 From 018870a06324b8ebc19c0d5ab3a209f872768306 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Mon, 28 Apr 2025 20:53:27 +0900 Subject: Fix some i18next call, which concatted translation and some string without i18next parameter --- src/routes/event.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/routes') diff --git a/src/routes/event.ts b/src/routes/event.ts index ca333c5..8972169 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -414,10 +414,10 @@ router.put( "

    " + i18next.t("routes.event.difftext") + "

      "; let displayDate; if (event.name !== updatedEvent.name) { - diffText += `
    • ` + i18next.t("routes.event.namechanged") + updatedEvent.name + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.namechanged", { eventname: updatedEvent.name} ) + `
    • `; } if (event.location !== updatedEvent.location) { - diffText += `
    • ` + i18next.t("routes.event.locationchanged") + updatedEvent.location + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.locationchanged", { location: updatedEvent.location} ) + `
    • `; } if ( event.start.toISOString() !== updatedEvent.start.toISOString() @@ -425,19 +425,19 @@ router.put( displayDate = moment .tz(updatedEvent.start, updatedEvent.timezone) .format(i18next.t("common.datetimeformat")); - diffText += `
    • ` + i18next.t("routes.event.starttimechanged") + displayDate + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.starttimechanged", { starttime: displayDate }) + `
    • `; } if (event.end.toISOString() !== updatedEvent.end.toISOString()) { displayDate = moment .tz(updatedEvent.end, updatedEvent.timezone) .format(i18next.t("common.datetimeformat")); - diffText += `
    • ` + i18next.t("routes.event.endtimechanged") + displayDate + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.endtimechanged", { endtime: displayDate }) + `
    • `; } if (event.timezone !== updatedEvent.timezone) { - diffText += `
    • ` + i18next.t("routes.event.timezonechanged") + updatedEvent.timezone + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.timezonechanged", { timezone: updatedEvent.timezone }) + `
    • `; } if (event.description !== updatedEvent.description) { - diffText += `
    • ` + i18next.t("routes.event.descriptionchanged") + `
    • `; + diffText += `
    • ` + i18next.t("routes.event.descriptionchanged", { description: updatedEvent.description }) + `
    • `; } diffText += `
    `; const updatedEventObject = await Event.findOneAndUpdate( @@ -502,7 +502,7 @@ router.put( sendEmailFromTemplate( config.general.email, attendeeEmails.join(","), - `${event.name} ` + i18next.t("routes.event.editedsubject"), + i18next.t("routes.event.editedsubject", { eventname: event.name}), "editEvent", { diffText, -- cgit v1.2.3 From fde84325ba98bbbab79cc5c88157457ee1cb0e42 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Sat, 3 May 2025 00:00:05 +0900 Subject: Rollback mail text when the event description changed --- src/routes/event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/routes') diff --git a/src/routes/event.ts b/src/routes/event.ts index 8972169..2116df8 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -437,7 +437,7 @@ router.put( diffText += `
  • ` + i18next.t("routes.event.timezonechanged", { timezone: updatedEvent.timezone }) + `
  • `; } if (event.description !== updatedEvent.description) { - diffText += `
  • ` + i18next.t("routes.event.descriptionchanged", { description: updatedEvent.description }) + `
  • `; + diffText += `
  • ` + i18next.t("routes.event.descriptionchanged") + `
  • `; } diffText += `
`; const updatedEventObject = await Event.findOneAndUpdate( -- cgit v1.2.3 From fb85d79dd2333cd6e0982e5ee0fdc1070ff99889 Mon Sep 17 00:00:00 2001 From: INOUE Daisuke Date: Wed, 28 May 2025 23:25:36 +0900 Subject: To pass final test --- src/routes/frontend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/routes') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index a64bce4..fca14c6 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -175,7 +175,7 @@ router.get("/:eventID", async (req: Request, res: Response) => { timezone: moment .tz(event.end, event.timezone) - .format(' (z)',) + .format('(z)',) }); } else { displayDate = i18next.t("frontend.displaydate-days", -- cgit v1.2.3