From b795d07ed7a1b705b72b171f8e8de267a720223b Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Sat, 7 Oct 2023 14:30:24 +0100 Subject: refactor: event form and api routes --- src/routes/frontend.ts | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/routes/frontend.ts') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 71984ec..d24210f 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -69,6 +69,13 @@ router.get("/:eventID", async (req: Request, res: Response) => { let parsedEnd = moment .tz(event.end, event.timezone) .format("YYYYMMDD[T]HHmmss"); + // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local + const parsedStartForDateInput = moment + .tz(event.start, event.timezone) + .format("YYYY-MM-DDTHH:mm"); + const parsedEndForDateInput = moment + .tz(event.end, event.timezone) + .format("YYYY-MM-DDTHH:mm"); let eventHasConcluded = false; if ( moment @@ -194,6 +201,8 @@ router.get("/:eventID", async (req: Request, res: Response) => { parsedLocation: parsedLocation, parsedStart: parsedStart, parsedEnd: parsedEnd, + parsedStartForDateInput, + parsedEndForDateInput, displayDate: displayDate, fromNow: fromNow, timezone: event.timezone, -- cgit v1.2.3 From b8e424a8602b586bbf346cd27171ede570a54973 Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Sat, 7 Oct 2023 18:48:08 +0100 Subject: refactor: frontend event group routes --- src/routes/frontend.ts | 183 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 2 deletions(-) (limited to 'src/routes/frontend.ts') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index d24210f..56ce4db 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -1,11 +1,12 @@ import { Router, Request, Response } from "express"; -import Event from "../models/Event.js"; import moment from "moment-timezone"; import { marked } from "marked"; import { frontendConfig } from "../util/config.js"; import { renderPlain } from "../util/markdown.js"; import getConfig from "../lib/config.js"; -import { addToLog } from "../helpers.js"; +import { addToLog, exportICal } from "../helpers.js"; +import Event from "../models/Event.js"; +import EventGroup from "../models/EventGroup.js"; const config = getConfig(); @@ -230,4 +231,182 @@ router.get("/:eventID", async (req: Request, res: Response) => { } }); +router.get("/group/:eventGroupID", async (req: Request, res: Response) => { + try { + const eventGroup = await EventGroup.findOne({ + id: req.params.eventGroupID, + }).lean(); + + if (!eventGroup) { + return res.status(404).render("404", { url: req.url }); + } + const parsedDescription = marked.parse(eventGroup.description); + const eventGroupEditToken = eventGroup.editToken; + const escapedName = eventGroup.name.replace(/\s+/g, "+"); + const eventGroupHasCoverImage = !!eventGroup.image; + const eventGroupHasHost = !!eventGroup.hostName; + + const events = await Event.find({ eventGroup: eventGroup._id }) + .lean() + .sort("start"); + + const updatedEvents = events.map((event) => { + const startMoment = moment.tz(event.start, event.timezone); + const endMoment = moment.tz(event.end, event.timezone); + const isSameDay = startMoment.isSame(endMoment, "day"); + + return { + id: event.id, + name: event.name, + displayDate: isSameDay + ? startMoment.format("D MMM YYYY") + : `${startMoment.format("D MMM YYYY")} - ${endMoment.format( + "D MMM YYYY", + )}`, + eventHasConcluded: endMoment.isBefore( + moment.tz(event.timezone), + ), + }; + }); + + const upcomingEventsExist = updatedEvents.some( + (e) => !e.eventHasConcluded, + ); + + let firstLoad = false; + if (eventGroup.firstLoad === true) { + firstLoad = true; + await EventGroup.findOneAndUpdate( + { id: req.params.eventGroupID }, + { firstLoad: false }, + ); + } + + let editingEnabled = false; + if (Object.keys(req.query).length !== 0) { + if (!req.query.e) { + editingEnabled = false; + } else { + editingEnabled = req.query.e === eventGroupEditToken; + } + } + + const metadata = { + title: eventGroup.name, + description: marked + .parse(eventGroup.description, { + renderer: renderPlain(), + }) + .split(" ") + .splice(0, 40) + .join(" ") + .trim(), + image: eventGroupHasCoverImage + ? `https://${config.general.domain}/events/` + eventGroup.image + : null, + url: `https://${config.general.domain}/` + req.params.eventID, + }; + + res.set("X-Robots-Tag", "noindex"); + res.render("eventgroup", { + domain: config.general.domain, + title: eventGroup.name, + eventGroupData: eventGroup, + escapedName: escapedName, + events: updatedEvents, + upcomingEventsExist: upcomingEventsExist, + parsedDescription: parsedDescription, + editingEnabled: editingEnabled, + eventGroupHasCoverImage: eventGroupHasCoverImage, + eventGroupHasHost: eventGroupHasHost, + firstLoad: firstLoad, + metadata: metadata, + }); + } catch (err) { + addToLog( + "displayEventGroup", + "error", + `Attempt to display event group ${req.params.eventGroupID} failed with error: ${err}`, + ); + console.log(err); + return res.status(404).render("404", { url: req.url }); + } +}); + +router.get( + "/group/:eventGroupID/feed.ics", + async (req: Request, res: Response) => { + try { + const eventGroup = await EventGroup.findOne({ + id: req.params.eventGroupID, + }).lean(); + + if (eventGroup) { + const events = await Event.find({ + eventGroup: eventGroup._id, + }).sort("start"); + const string = exportICal(events, eventGroup.name); + res.set("Content-Type", "text/calendar"); + res.send(string); + } + } catch (err) { + addToLog( + "eventGroupFeed", + "error", + `Attempt to display event group feed for ${req.params.eventGroupID} failed with error: ${err}`, + ); + console.log(err); + res.status(404).render("404", { url: req.url }); + } + }, +); + +router.get("/export/event/:eventID", async (req: Request, res: Response) => { + try { + const event = await Event.findOne({ + id: req.params.eventID, + }).populate("eventGroup"); + + if (event) { + const string = exportICal([event], event.name); + res.send(string); + } + } catch (err) { + addToLog( + "exportEvent", + "error", + `Attempt to export event ${req.params.eventID} failed with error: ${err}`, + ); + console.log(err); + res.status(404).render("404", { url: req.url }); + } +}); + +router.get( + "/export/group/:eventGroupID", + async (req: Request, res: Response) => { + try { + const eventGroup = await EventGroup.findOne({ + id: req.params.eventGroupID, + }).lean(); + + if (eventGroup) { + const events = await Event.find({ + eventGroup: eventGroup._id, + }).sort("start"); + const string = exportICal(events, eventGroup.name); + res.send(string); + } + } catch (err) { + addToLog( + "exportEvent", + "error", + `Attempt to export event group ${req.params.eventGroupID} failed with error: ${err}`, + ); + console.log(err); + res.status(404).render("404", { url: req.url }); + } + }, +); + export default router; -- cgit v1.2.3 From 6b220e094f215c488eb5102e25506f5b3d371245 Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Sun, 8 Oct 2023 12:11:36 +0100 Subject: Refactor: event group form and API, extract JS --- src/routes/frontend.ts | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src/routes/frontend.ts') diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 56ce4db..c9594ef 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -6,7 +6,7 @@ import { renderPlain } from "../util/markdown.js"; import getConfig from "../lib/config.js"; import { addToLog, exportICal } from "../helpers.js"; import Event from "../models/Event.js"; -import EventGroup from "../models/EventGroup.js"; +import EventGroup, { IEventGroup } from "../models/EventGroup.js"; const config = getConfig(); @@ -215,6 +215,31 @@ router.get("/:eventID", async (req: Request, res: Response) => { eventHasConcluded: eventHasConcluded, eventHasBegun: eventHasBegun, metadata: metadata, + jsonData: { + name: event.name, + id: event.id, + description: event.description, + location: event.location, + timezone: event.timezone, + url: event.url, + hostName: event.hostName, + creatorEmail: event.creatorEmail, + eventGroupID: event.eventGroup + ? (event.eventGroup as unknown as IEventGroup).id + : null, + eventGroupEditToken: event.eventGroup + ? (event.eventGroup as unknown as IEventGroup).editToken + : null, + usersCanAttend: event.usersCanAttend, + usersCanComment: event.usersCanComment, + maxAttendees: event.maxAttendees, + startISO: eventStartISO, + endISO: eventEndISO, + startForDateInput: parsedStartForDateInput, + endForDateInput: parsedEndForDateInput, + image: event.image, + editToken: editingEnabled ? eventEditToken : null, + }, }); } } catch (err) { @@ -321,6 +346,16 @@ router.get("/group/:eventGroupID", async (req: Request, res: Response) => { eventGroupHasHost: eventGroupHasHost, firstLoad: firstLoad, metadata: metadata, + jsonData: { + name: eventGroup.name, + id: eventGroup.id, + description: eventGroup.description, + url: eventGroup.url, + hostName: eventGroup.hostName, + creatorEmail: eventGroup.creatorEmail, + image: eventGroup.image, + editToken: editingEnabled ? eventGroupEditToken : null, + }, }); } catch (err) { addToLog( -- cgit v1.2.3