From 5980e63de0d71f2168d4c234312f1e3d6ab23d8c Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Fri, 6 Oct 2023 16:16:26 +0100 Subject: Remove unused Handlebars files --- views/login.handlebars | 32 ------------------------------ views/partials/sidebar-dropdown.handlebars | 11 ---------- views/register.handlebars | 15 -------------- 3 files changed, 58 deletions(-) delete mode 100755 views/login.handlebars delete mode 100755 views/partials/sidebar-dropdown.handlebars delete mode 100755 views/register.handlebars (limited to 'views') diff --git a/views/login.handlebars b/views/login.handlebars deleted file mode 100755 index a08f5b4..0000000 --- a/views/login.handlebars +++ /dev/null @@ -1,32 +0,0 @@ -

Log in

-
-
- {{#if isPublic}} -

Register or log in to your account to be able to show this event on your profile, edit it, and delete it. Associating an event with an account is optional, but keep in mind that an unassociated event can not be modified in any way and will be automatically deleted 30 days after it concludes.

- {{else if isPrivate}} -

You must register or log in to your account to create a private event.

- {{else if isOrganisation}} -

You must register or log in to an organisation account to create an organisation event.

- {{/if}} -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- - -

- Register | Forgot password -

diff --git a/views/partials/sidebar-dropdown.handlebars b/views/partials/sidebar-dropdown.handlebars deleted file mode 100755 index 9f9e564..0000000 --- a/views/partials/sidebar-dropdown.handlebars +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/views/register.handlebars b/views/register.handlebars deleted file mode 100755 index 1e097c1..0000000 --- a/views/register.handlebars +++ /dev/null @@ -1,15 +0,0 @@ -
- -
- -
-
-
- -
- -
-
-
-
-
\ No newline at end of file -- cgit v1.2.3 From 115210bfd9a5ae7bb8b516ce0d2cf3d9042dead7 Mon Sep 17 00:00:00 2001 From: Raphael Kabo Date: Fri, 6 Oct 2023 16:18:13 +0100 Subject: refactor: frontend routes into TS file, new event path --- scripts/docker-test.sh | 2 +- src/app.ts | 5 +- src/routes.js | 294 +-------------------------------- src/routes/frontend.ts | 220 ++++++++++++++++++++++++ views/optionsform.handlebars | 2 +- views/partials/neweventform.handlebars | 12 -- views/partials/sidebar.handlebars | 2 +- 7 files changed, 229 insertions(+), 308 deletions(-) create mode 100644 src/routes/frontend.ts (limited to 'views') diff --git a/scripts/docker-test.sh b/scripts/docker-test.sh index 1cba254..f071740 100755 --- a/scripts/docker-test.sh +++ b/scripts/docker-test.sh @@ -15,6 +15,6 @@ trap cleanup 0 docker-compose up --build & while [[ "$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/)" -ne "200" ]]; do sleep 5; done -curl -v http://localhost:3000/new/event/public +curl -v http://localhost:3000/new cleanup \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 32e89b6..f49092c 100755 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,9 @@ import express from "express"; -import routes from "./routes.js"; import hbs from "express-handlebars"; +import routes from "./routes.js"; +import frontend from "./routes/frontend.js"; + const app = express(); // View engine // @@ -39,6 +41,7 @@ app.use(express.json({ type: "application/activity+json" })); // support json en app.use(express.urlencoded({ extended: true })); // Router // +app.use("/", frontend); app.use("/", routes); export default app; diff --git a/src/routes.js b/src/routes.js index 55436c9..be0dcde 100755 --- a/src/routes.js +++ b/src/routes.js @@ -51,47 +51,6 @@ const nanoid = customAlphabet( const router = express.Router(); -// Extra marked renderer (used to render plaintext event description for page metadata) -// Adapted from https://dustinpfister.github.io/2017/11/19/nodejs-marked/ -// ? to ? helper -function htmlEscapeToText(text) { - return text.replace(/\&\#[0-9]*;|&/g, function (escapeCode) { - if (escapeCode.match(/amp/)) { - return "&"; - } - return String.fromCharCode(escapeCode.match(/[0-9]+/)); - }); -} - -function render_plain() { - var render = new marked.Renderer(); - // render just the text of a link, strong, em - render.link = function (href, title, text) { - return text; - }; - render.strong = function (text) { - return text; - }; - render.em = function (text) { - return text; - }; - // render just the text of a paragraph - render.paragraph = function (text) { - return htmlEscapeToText(text) + "\r\n"; - }; - // render nothing for headings, images, and br - render.heading = function (text, level) { - return ""; - }; - render.image = function (href, title, text) { - return ""; - }; - render.br = function () { - return ""; - }; - return render; -} - let sendEmails = false; let nodemailerTransporter; if (config.general.mail_service) { @@ -224,46 +183,6 @@ schedule.scheduleJob("59 23 * * *", function (fireDate) { // old (they're not going to become active) }); -// FRONTEND ROUTES - -router.get("/", (req, res) => { - res.render("home", { - domain, - email: contactEmail, - siteName, - showKofi, - }); -}); - -router.get("/new", (req, res) => { - res.render("home"); -}); - -router.get("/new/event", (req, res) => { - res.render("newevent", { - domain: domain, - email: contactEmail, - siteName: siteName, - }); -}); -router.get("/new/event/public", (req, res) => { - let isPrivate = false; - let isPublic = true; - let isOrganisation = false; - let isUnknownType = false; - res.render("newevent", { - title: "New event", - isPrivate: isPrivate, - isPublic: isPublic, - isOrganisation: isOrganisation, - isUnknownType: isUnknownType, - eventType: "public", - domain: domain, - email: contactEmail, - siteName: siteName, - }); -}); - // return the JSON for the featured/pinned post for this event router.get("/:eventID/featured", (req, res) => { if (!isFederated) return res.sendStatus(404); @@ -390,216 +309,6 @@ router.get("/.well-known/webfinger", (req, res) => { } }); -router.get("/:eventID", (req, res) => { - Event.findOne({ - id: req.params.eventID, - }) - .lean() // Required, see: https://stackoverflow.com/questions/59690923/handlebars-access-has-been-denied-to-resolve-the-property-from-because-it-is - .populate("eventGroup") - .then((event) => { - if (event) { - const parsedLocation = event.location.replace(/\s+/g, "+"); - let displayDate; - 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)[]' - ); - } 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)[]' - ); - } - let eventStartISO = moment.tz(event.start, "Etc/UTC").toISOString(); - let eventEndISO = moment.tz(event.end, "Etc/UTC").toISOString(); - let parsedStart = moment - .tz(event.start, event.timezone) - .format("YYYYMMDD[T]HHmmss"); - let parsedEnd = moment - .tz(event.end, event.timezone) - .format("YYYYMMDD[T]HHmmss"); - let eventHasConcluded = false; - if ( - moment - .tz(event.end, event.timezone) - .isBefore(moment.tz(event.timezone)) - ) { - eventHasConcluded = true; - } - let eventHasBegun = false; - if ( - moment - .tz(event.start, event.timezone) - .isBefore(moment.tz(event.timezone)) - ) { - eventHasBegun = true; - } - let fromNow = moment.tz(event.start, event.timezone).fromNow(); - let parsedDescription = marked.parse(event.description); - let eventEditToken = event.editToken; - - let escapedName = event.name.replace(/\s+/g, "+"); - - let eventHasCoverImage = false; - if (event.image) { - eventHasCoverImage = true; - } else { - eventHasCoverImage = false; - } - let eventHasHost = false; - if (event.hostName) { - eventHasHost = true; - } else { - eventHasHost = false; - } - let firstLoad = false; - if (event.firstLoad === true) { - firstLoad = true; - Event.findOneAndUpdate( - { id: req.params.eventID }, - { firstLoad: false }, - function (err, raw) { - if (err) { - res.send(err); - } - } - ); - } - let editingEnabled = false; - if (Object.keys(req.query).length !== 0) { - if (!req.query.e) { - editingEnabled = false; - console.log("No edit token set"); - } else { - if (req.query.e === eventEditToken) { - editingEnabled = true; - } else { - editingEnabled = false; - } - } - } - let eventAttendees = event.attendees - .sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)) - .map((el) => { - if (!el.id) { - el.id = el._id; - } - if (el.number > 1) { - el.name = `${el.name} (${el.number} people)`; - } - return el; - }) - .filter((obj, pos, arr) => { - return ( - obj.status === "attending" && - arr.map((mapObj) => mapObj.id).indexOf(obj.id) === pos - ); - }); - - let spotsRemaining, noMoreSpots; - let numberOfAttendees = eventAttendees.reduce((acc, attendee) => { - if (attendee.status === "attending") { - return acc + attendee.number || 1; - } - return acc; - }, 0); - if (event.maxAttendees) { - spotsRemaining = event.maxAttendees - numberOfAttendees; - if (spotsRemaining <= 0) { - noMoreSpots = true; - } - } - let metadata = { - title: event.name, - description: marked - .parse(event.description, { renderer: render_plain() }) - .split(" ") - .splice(0, 40) - .join(" ") - .trim(), - image: eventHasCoverImage - ? `https://${domain}/events/` + event.image - : null, - url: `https://${domain}/` + req.params.eventID, - }; - if ( - req.headers.accept && - (req.headers.accept.includes("application/activity+json") || - req.headers.accept.includes("application/json") || - req.headers.accept.includes("application/json+ld")) - ) { - res - .header("Content-Type", "application/activity+json") - .send(JSON.parse(event.activityPubActor)); - } else { - res.set("X-Robots-Tag", "noindex"); - res.render("event", { - domain: domain, - isFederated: isFederated, - email: contactEmail, - title: event.name, - escapedName: escapedName, - eventData: event, - eventAttendees: eventAttendees, - numberOfAttendees, - spotsRemaining: spotsRemaining, - noMoreSpots: noMoreSpots, - eventStartISO: eventStartISO, - eventEndISO: eventEndISO, - parsedLocation: parsedLocation, - parsedStart: parsedStart, - parsedEnd: parsedEnd, - displayDate: displayDate, - fromNow: fromNow, - timezone: event.timezone, - parsedDescription: parsedDescription, - editingEnabled: editingEnabled, - eventHasCoverImage: eventHasCoverImage, - eventHasHost: eventHasHost, - firstLoad: firstLoad, - eventHasConcluded: eventHasConcluded, - eventHasBegun: eventHasBegun, - metadata: metadata, - siteName: siteName, - }); - } - } else { - res.status(404); - res.render("404", { url: req.url }); - } - }) - .catch((err) => { - addToLog( - "displayEvent", - "error", - "Attempt to display event " + - req.params.eventID + - " failed with error: " + - err - ); - console.log(err); - res.status(404); - res.render("404", { url: req.url }); - return; - }); -}); - router.get("/:eventID/followers", (req, res) => { if (!isFederated) return res.sendStatus(404); const eventID = req.params.eventID; @@ -917,7 +626,7 @@ router.post("/newevent", async (req, res) => { const event = new Event({ id: eventID, - type: req.body.eventType, + type: "public", // This is for backwards compatibility name: req.body.eventName, location: req.body.eventLocation, start: startUTC, @@ -1082,6 +791,7 @@ router.post("/newevent", async (req, res) => { res.end(); }) .catch((err) => { + console.error(err); res.status(500).send("Database error, please try again :( - " + err); addToLog( "createEvent", diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts new file mode 100644 index 0000000..9dea619 --- /dev/null +++ b/src/routes/frontend.ts @@ -0,0 +1,220 @@ +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"; + +const config = getConfig(); + +const router = Router(); +router.get("/", (_: Request, res: Response) => { + res.render("home", frontendConfig()); +}); + +router.get("/new", (_: Request, res: Response) => { + res.render("newevent", { + title: "New event", + ...frontendConfig(), + }); +}); + +router.get("/:eventID", async (req: Request, res: Response) => { + try { + const event = await Event.findOne({ + id: req.params.eventID, + }) + .lean() // Required, see: https://stackoverflow.com/questions/59690923/handlebars-access-has-been-denied-to-resolve-the-property-from-because-it-is + .populate("eventGroup"); + if (!event) { + res.status(404); + res.render("404", { url: req.url }); + return; + } + const parsedLocation = event.location.replace(/\s+/g, "+"); + let displayDate; + 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)[]' + ); + } 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)[]' + ); + } + let eventStartISO = moment.tz(event.start, "Etc/UTC").toISOString(); + let eventEndISO = moment.tz(event.end, "Etc/UTC").toISOString(); + let parsedStart = moment + .tz(event.start, event.timezone) + .format("YYYYMMDD[T]HHmmss"); + let parsedEnd = moment + .tz(event.end, event.timezone) + .format("YYYYMMDD[T]HHmmss"); + let eventHasConcluded = false; + if ( + moment.tz(event.end, event.timezone).isBefore(moment.tz(event.timezone)) + ) { + eventHasConcluded = true; + } + let eventHasBegun = false; + if ( + moment.tz(event.start, event.timezone).isBefore(moment.tz(event.timezone)) + ) { + eventHasBegun = true; + } + let fromNow = moment.tz(event.start, event.timezone).fromNow(); + let parsedDescription = marked.parse(event.description); + let eventEditToken = event.editToken; + + let escapedName = event.name.replace(/\s+/g, "+"); + + let eventHasCoverImage = false; + if (event.image) { + eventHasCoverImage = true; + } else { + eventHasCoverImage = false; + } + let eventHasHost = false; + if (event.hostName) { + eventHasHost = true; + } else { + eventHasHost = false; + } + let firstLoad = false; + if (event.firstLoad === true) { + firstLoad = true; + await Event.findOneAndUpdate( + { id: req.params.eventID }, + { firstLoad: false } + ); + } + let editingEnabled = false; + if (Object.keys(req.query).length !== 0) { + if (!req.query.e) { + editingEnabled = false; + console.log("No edit token set"); + } else { + if (req.query.e === eventEditToken) { + editingEnabled = true; + } else { + editingEnabled = false; + } + } + } + let eventAttendees = event.attendees + ?.sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)) + .map((el) => { + if (!el.id) { + el.id = el._id; + } + if (el.number && el.number > 1) { + el.name = `${el.name} (${el.number} people)`; + } + return el; + }) + .filter((obj, pos, arr) => { + return ( + obj.status === "attending" && + arr.map((mapObj) => mapObj.id).indexOf(obj.id) === pos + ); + }); + + let spotsRemaining, noMoreSpots; + let numberOfAttendees = + eventAttendees?.reduce((acc, attendee) => { + if (attendee.status === "attending") { + return acc + (attendee.number || 1); + } + return acc; + }, 0) || 0; + if (event.maxAttendees) { + spotsRemaining = event.maxAttendees - numberOfAttendees; + if (spotsRemaining <= 0) { + noMoreSpots = true; + } + } + let metadata = { + title: event.name, + description: marked + .parse(event.description, { renderer: renderPlain() }) + .split(" ") + .splice(0, 40) + .join(" ") + .trim(), + image: eventHasCoverImage + ? `https://${config.general.domain}/events/` + event.image + : null, + url: `https://${config.general.domain}/` + req.params.eventID, + }; + if ( + req.headers.accept && + (req.headers.accept.includes("application/activity+json") || + req.headers.accept.includes("application/json") || + req.headers.accept.includes("application/json+ld")) + ) { + res + .header("Content-Type", "application/activity+json") + .send(JSON.parse(event.activityPubActor || "{}")); + } else { + res.set("X-Robots-Tag", "noindex"); + res.render("event", { + ...frontendConfig(), + title: event.name, + escapedName: escapedName, + eventData: event, + eventAttendees: eventAttendees, + numberOfAttendees, + spotsRemaining: spotsRemaining, + noMoreSpots: noMoreSpots, + eventStartISO: eventStartISO, + eventEndISO: eventEndISO, + parsedLocation: parsedLocation, + parsedStart: parsedStart, + parsedEnd: parsedEnd, + displayDate: displayDate, + fromNow: fromNow, + timezone: event.timezone, + parsedDescription: parsedDescription, + editingEnabled: editingEnabled, + eventHasCoverImage: eventHasCoverImage, + eventHasHost: eventHasHost, + firstLoad: firstLoad, + eventHasConcluded: eventHasConcluded, + eventHasBegun: eventHasBegun, + metadata: metadata, + }); + } + } catch (err) { + addToLog( + "displayEvent", + "error", + "Attempt to display event " + + req.params.eventID + + " failed with error: " + + err + ); + console.log(err); + res.status(404).render("404", { url: req.url }); + } +}); + +export default router; diff --git a/views/optionsform.handlebars b/views/optionsform.handlebars index a844d12..85ebd9f 100755 --- a/views/optionsform.handlebars +++ b/views/optionsform.handlebars @@ -10,7 +10,7 @@
diff --git a/views/partials/neweventform.handlebars b/views/partials/neweventform.handlebars index d456d2e..3c7e060 100755 --- a/views/partials/neweventform.handlebars +++ b/views/partials/neweventform.handlebars @@ -1,6 +1,5 @@

Create an event

-
@@ -54,17 +53,6 @@ Recommended dimensions (w x h): 920px by 300px.
- {{#unless isPublic}} -
- -
- -
-
-
-
-
- {{/unless}}
diff --git a/views/partials/sidebar.handlebars b/views/partials/sidebar.handlebars index 1aa0f74..980e699 100755 --- a/views/partials/sidebar.handlebars +++ b/views/partials/sidebar.handlebars @@ -3,5 +3,5 @@

Nicer events

- New event + New event
-- cgit v1.2.3