summaryrefslogtreecommitdiff
path: root/src/routes.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes.js')
-rwxr-xr-xsrc/routes.js757
1 files changed, 3 insertions, 754 deletions
diff --git a/src/routes.js b/src/routes.js
index 7257bdb..94b7477 100755
--- a/src/routes.js
+++ b/src/routes.js
@@ -6,7 +6,6 @@ import { getConfig } from "./lib/config.js";
import { addToLog, exportIcal } from "./helpers.js";
import moment from "moment-timezone";
import { marked } from "marked";
-import generateRSAKeypair from "generate-rsa-keypair";
import crypto from "crypto";
import request from "request";
import niceware from "niceware";
@@ -17,21 +16,14 @@ import fileUpload from "express-fileupload";
import Jimp from "jimp";
import schedule from "node-schedule";
import {
- createActivityPubActor,
- createActivityPubEvent,
- createFeaturedPost,
- createWebfinger,
- updateActivityPubActor,
- updateActivityPubEvent,
broadcastCreateMessage,
- broadcastUpdateMessage,
broadcastDeleteMessage,
- sendDirectMessage,
processInbox,
} from "./activitypub.js";
import Event from "./models/Event.js";
import EventGroup from "./models/EventGroup.js";
import path from "path";
+import { renderPlain } from "./util/markdown.js";
const config = getConfig();
const domain = config.general.domain;
@@ -40,7 +32,6 @@ 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;
-const showKofi = config.general.show_kofi;
// This alphabet (used to generate all event, group, etc. IDs) is missing '-'
// because ActivityPub doesn't like it in IDs
@@ -193,180 +184,6 @@ schedule.scheduleJob("59 23 * * *", function (fireDate) {
// old (they're not going to become active)
});
-// return the JSON for the featured/pinned post for this event
-router.get("/:eventID/featured", (req, res) => {
- if (!isFederated) return res.sendStatus(404);
- const { eventID } = req.params;
- const guidObject = crypto.randomBytes(16).toString("hex");
- const featured = {
- "@context": "https://www.w3.org/ns/activitystreams",
- id: `https://${domain}/${eventID}/featured`,
- type: "OrderedCollection",
- orderedItems: [createFeaturedPost(eventID)],
- };
- if (
- req.headers.accept &&
- (req.headers.accept.includes("application/activity+json") ||
- req.headers.accept.includes("application/ld+json"))
- ) {
- res.header("Content-Type", "application/activity+json").send(featured);
- } else {
- res.header("Content-Type", "application/json").send(featured);
- }
-});
-
-// return the JSON for a given activitypub message
-router.get("/:eventID/m/:hash", (req, res) => {
- if (!isFederated) return res.sendStatus(404);
- const { hash, eventID } = req.params;
- const id = `https://${domain}/${eventID}/m/${hash}`;
-
- Event.findOne({
- id: eventID,
- })
- .then((event) => {
- if (!event) {
- res.status(404);
- res.render("404", { url: req.url });
- } else {
- const message = event.activityPubMessages.find(
- (el) => el.id === id,
- );
- if (message) {
- if (
- req.headers.accept &&
- (req.headers.accept.includes(
- "application/activity+json",
- ) ||
- req.headers.accept.includes("application/ld+json"))
- ) {
- res.header(
- "Content-Type",
- "application/activity+json",
- ).send(JSON.parse(message.content));
- } else {
- res.header("Content-Type", "application/json").send(
- JSON.parse(message.content),
- );
- }
- } else {
- res.status(404);
- return res.render("404", { url: req.url });
- }
- }
- })
- .catch((err) => {
- addToLog(
- "getActivityPubMessage",
- "error",
- "Attempt to get Activity Pub Message for " +
- id +
- " failed with error: " +
- err,
- );
- res.status(404);
- res.render("404", { url: req.url });
- return;
- });
-});
-
-// return the webfinger record required for the initial activitypub handshake
-router.get("/.well-known/webfinger", (req, res) => {
- if (!isFederated) return res.sendStatus(404);
- let resource = req.query.resource;
- if (!resource || !resource.includes("acct:")) {
- return res
- .status(400)
- .send(
- 'Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.',
- );
- } else {
- // "foo@domain"
- let activityPubAccount = resource.replace("acct:", "");
- // "foo"
- let eventID = activityPubAccount.replace(/@.*/, "");
- Event.findOne({
- id: eventID,
- })
- .then((event) => {
- if (!event) {
- res.status(404);
- res.render("404", { url: req.url });
- } else {
- if (
- req.headers.accept &&
- (req.headers.accept.includes(
- "application/activity+json",
- ) ||
- req.headers.accept.includes("application/ld+json"))
- ) {
- res.header(
- "Content-Type",
- "application/activity+json",
- ).send(createWebfinger(eventID, domain));
- } else {
- res.header("Content-Type", "application/json").send(
- createWebfinger(eventID, domain),
- );
- }
- }
- })
- .catch((err) => {
- addToLog(
- "renderWebfinger",
- "error",
- "Attempt to render webfinger for " +
- req.params.eventID +
- " failed with error: " +
- 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;
- Event.findOne({
- id: eventID,
- }).then((event) => {
- if (event) {
- const followers = event.followers.map((el) => el.actorId);
- let followersCollection = {
- type: "OrderedCollection",
- totalItems: followers.length,
- id: `https://${domain}/${eventID}/followers`,
- first: {
- type: "OrderedCollectionPage",
- totalItems: followers.length,
- partOf: `https://${domain}/${eventID}/followers`,
- orderedItems: followers,
- id: `https://${domain}/${eventID}/followers?page=1`,
- },
- "@context": ["https://www.w3.org/ns/activitystreams"],
- };
- if (
- req.headers.accept &&
- (req.headers.accept.includes("application/activity+json") ||
- req.headers.accept.includes("application/ld+json"))
- ) {
- return res
- .header("Content-Type", "application/activity+json")
- .send(followersCollection);
- } else {
- return res
- .header("Content-Type", "application/json")
- .send(followersCollection);
- }
- } else {
- return res.status(400).send("Bad request.");
- }
- });
-});
-
router.get("/group/:eventGroupID", (req, res) => {
EventGroup.findOne({
id: req.params.eventGroupID,
@@ -467,7 +284,7 @@ router.get("/group/:eventGroupID", (req, res) => {
title: eventGroup.name,
description: marked
.parse(eventGroup.description, {
- renderer: render_plain(),
+ renderer: renderPlain(),
})
.split(" ")
.splice(0, 40)
@@ -603,243 +420,6 @@ router.get("/exportgroup/:eventGroupID", (req, res) => {
});
// BACKEND ROUTES
-
-router.post("/newevent", async (req, res) => {
- let eventID = nanoid();
- let editToken = randomstring.generate();
- let eventImageFilename = "";
- let isPartOfEventGroup = false;
- if (req.files && Object.keys(req.files).length !== 0) {
- let eventImageBuffer = req.files.imageUpload.data;
- eventImageFilename = await Jimp.read(eventImageBuffer)
- .then((img) => {
- img.resize(920, Jimp.AUTO) // resize
- .quality(80) // set JPEG quality
- .write("./public/events/" + eventID + ".jpg"); // save
- const filename = eventID + ".jpg";
- return filename;
- })
- .catch((err) => {
- addToLog(
- "Jimp",
- "error",
- "Attempt to edit image failed with error: " + err,
- );
- });
- }
- let startUTC = moment.tz(
- req.body.eventStart,
- "D MMMM YYYY, hh:mm a",
- req.body.timezone,
- );
- let endUTC = moment.tz(
- req.body.eventEnd,
- "D MMMM YYYY, hh:mm a",
- req.body.timezone,
- );
- let eventGroup;
- if (req.body.eventGroupCheckbox) {
- eventGroup = await EventGroup.findOne({
- id: req.body.eventGroupID,
- editToken: req.body.eventGroupEditToken,
- });
- if (eventGroup) {
- isPartOfEventGroup = true;
- }
- }
-
- // generate RSA keypair for ActivityPub
- let pair = generateRSAKeypair();
-
- const event = new Event({
- id: eventID,
- type: "public", // This is for backwards compatibility
- name: req.body.eventName,
- location: req.body.eventLocation,
- start: startUTC,
- end: endUTC,
- timezone: req.body.timezone,
- description: req.body.eventDescription,
- image: eventImageFilename,
- creatorEmail: req.body.creatorEmail,
- url: req.body.eventURL,
- hostName: req.body.hostName,
- viewPassword: req.body.viewPassword,
- editPassword: req.body.editPassword,
- editToken: editToken,
- eventGroup: isPartOfEventGroup ? eventGroup._id : null,
- usersCanAttend: req.body.joinCheckbox ? true : false,
- showUsersList: req.body.guestlistCheckbox ? true : false,
- usersCanComment: req.body.interactionCheckbox ? true : false,
- maxAttendees: req.body.maxAttendees,
- firstLoad: true,
- activityPubActor: createActivityPubActor(
- eventID,
- domain,
- pair.public,
- marked.parse(req.body.eventDescription),
- req.body.eventName,
- req.body.eventLocation,
- eventImageFilename,
- startUTC,
- endUTC,
- req.body.timezone,
- ),
- activityPubEvent: createActivityPubEvent(
- req.body.eventName,
- startUTC,
- endUTC,
- req.body.timezone,
- req.body.eventDescription,
- req.body.eventLocation,
- ),
- activityPubMessages: [
- {
- id: `https://${domain}/${eventID}/m/featuredPost`,
- content: JSON.stringify(
- createFeaturedPost(
- eventID,
- req.body.eventName,
- startUTC,
- endUTC,
- req.body.timezone,
- req.body.eventDescription,
- req.body.eventLocation,
- ),
- ),
- },
- ],
- publicKey: pair.public,
- privateKey: pair.private,
- });
- event
- .save()
- .then((event) => {
- addToLog("createEvent", "success", "Event " + eventID + "created");
- // Send email with edit link
- if (req.body.creatorEmail && sendEmails) {
- req.app.get("hbsInstance").renderView(
- "./views/emails/createevent.handlebars",
- {
- eventID,
- editToken,
- siteName,
- siteLogo,
- domain,
- cache: true,
- layout: "email.handlebars",
- },
- function (err, html) {
- const msg = {
- to: req.body.creatorEmail,
- from: {
- name: siteName,
- email: contactEmail,
- address: contactEmail,
- },
- subject: `${siteName}: ${req.body.eventName}`,
- html,
- };
- switch (mailService) {
- case "sendgrid":
- sgMail.send(msg).catch((e) => {
- console.error(e.toString());
- res.status(500).end();
- });
- break;
- case "nodemailer":
- nodemailerTransporter
- .sendMail(msg)
- .catch((e) => {
- console.error(e.toString());
- res.status(500).end();
- });
- break;
- }
- },
- );
- }
- // If the event was added to a group, send an email to any group
- // subscribers
- if (event.eventGroup && sendEmails) {
- EventGroup.findOne({ _id: event.eventGroup._id }).then(
- (eventGroup) => {
- const subscribers = eventGroup.subscribers.reduce(
- (acc, current) => {
- if (acc.includes(current.email)) {
- return acc;
- }
- return [current.email, ...acc];
- },
- [],
- );
- subscribers.forEach((emailAddress) => {
- req.app.get("hbsInstance").renderView(
- "./views/emails/eventgroupupdated.handlebars",
- {
- siteName,
- siteLogo,
- domain,
- eventID: req.params.eventID,
- eventGroupName: eventGroup.name,
- eventName: event.name,
- eventID: event.id,
- eventGroupID: eventGroup.id,
- emailAddress:
- encodeURIComponent(emailAddress),
- cache: true,
- layout: "email.handlebars",
- },
- function (err, html) {
- const msg = {
- to: emailAddress,
- from: {
- name: siteName,
- email: contactEmail,
- },
- subject: `${siteName}: New event in ${eventGroup.name}`,
- html,
- };
- switch (mailService) {
- case "sendgrid":
- sgMail.send(msg).catch((e) => {
- console.error(e.toString());
- res.status(500).end();
- });
- break;
- case "nodemailer":
- nodemailerTransporter
- .sendMail(msg)
- .catch((e) => {
- console.error(e.toString());
- res.status(500).end();
- });
- break;
- }
- },
- );
- });
- },
- );
- }
- res.writeHead(302, {
- Location: "/" + eventID + "?e=" + editToken,
- });
- res.end();
- })
- .catch((err) => {
- console.error(err);
- res.status(500).send(
- "Database error, please try again :( - " + err,
- );
- addToLog(
- "createEvent",
- "error",
- "Attempt to create event failed with error: " + err,
- );
- });
-});
-
router.post("/importevent", (req, res) => {
let eventID = nanoid();
let editToken = randomstring.generate();
@@ -1071,338 +651,6 @@ router.post("/verifytoken/group/:eventGroupID", (req, res) => {
});
});
-router.post("/editevent/:eventID/:editToken", (req, res) => {
- let submittedEditToken = req.params.editToken;
- Event.findOne({
- id: req.params.eventID,
- })
- .then(async (event) => {
- if (event.editToken === submittedEditToken) {
- // Token matches
-
- // If there is a new image, upload that first
- let eventID = req.params.eventID;
- let eventImageFilename = event.image;
- if (req.files && Object.keys(req.files).length !== 0) {
- let eventImageBuffer = req.files.imageUpload.data;
- Jimp.read(eventImageBuffer, (err, img) => {
- if (err) throw err;
- img.resize(920, Jimp.AUTO) // resize
- .quality(80) // set JPEG
- .write("./public/events/" + eventID + ".jpg"); // save
- });
- eventImageFilename = eventID + ".jpg";
- }
- let startUTC = moment.tz(
- req.body.eventStart,
- "D MMMM YYYY, hh:mm a",
- req.body.timezone,
- );
- let endUTC = moment.tz(
- req.body.eventEnd,
- "D MMMM YYYY, hh:mm a",
- req.body.timezone,
- );
-
- let isPartOfEventGroup = false;
- let eventGroup;
- if (req.body.eventGroupCheckbox) {
- eventGroup = await EventGroup.findOne({
- id: req.body.eventGroupID,
- editToken: req.body.eventGroupEditToken,
- });
- if (eventGroup) {
- isPartOfEventGroup = true;
- }
- }
- const updatedEvent = {
- name: req.body.eventName,
- location: req.body.eventLocation,
- start: startUTC,
- end: endUTC,
- timezone: req.body.timezone,
- description: req.body.eventDescription,
- url: req.body.eventURL,
- hostName: req.body.hostName,
- image: eventImageFilename,
- usersCanAttend: req.body.joinCheckbox ? true : false,
- showUsersList: req.body.guestlistCheckbox ? true : false,
- usersCanComment: req.body.interactionCheckbox
- ? true
- : false,
- maxAttendees: req.body.maxAttendeesCheckbox
- ? req.body.maxAttendees
- : null,
- eventGroup: isPartOfEventGroup ? eventGroup._id : null,
- activityPubActor: event.activityPubActor
- ? updateActivityPubActor(
- JSON.parse(event.activityPubActor),
- req.body.eventDescription,
- req.body.eventName,
- req.body.eventLocation,
- eventImageFilename,
- startUTC,
- endUTC,
- req.body.timezone,
- )
- : null,
- activityPubEvent: event.activityPubEvent
- ? updateActivityPubEvent(
- JSON.parse(event.activityPubEvent),
- req.body.eventName,
- req.body.startUTC,
- req.body.endUTC,
- req.body.timezone,
- )
- : null,
- };
- let diffText =
- "<p>This event was just updated with new information.</p><ul>";
- let displayDate;
- if (event.name !== updatedEvent.name) {
- diffText += `<li>the event name changed to ${updatedEvent.name}</li>`;
- }
- if (event.location !== updatedEvent.location) {
- diffText += `<li>the location changed to ${updatedEvent.location}</li>`;
- }
- if (
- event.start.toISOString() !==
- updatedEvent.start.toISOString()
- ) {
- displayDate = moment
- .tz(updatedEvent.start, updatedEvent.timezone)
- .format("dddd D MMMM YYYY h:mm a");
- diffText += `<li>the start time changed to ${displayDate}</li>`;
- }
- if (
- event.end.toISOString() !== updatedEvent.end.toISOString()
- ) {
- displayDate = moment
- .tz(updatedEvent.end, updatedEvent.timezone)
- .format("dddd D MMMM YYYY h:mm a");
- diffText += `<li>the end time changed to ${displayDate}</li>`;
- }
- if (event.timezone !== updatedEvent.timezone) {
- diffText += `<li>the time zone changed to ${updatedEvent.timezone}</li>`;
- }
- if (event.description !== updatedEvent.description) {
- diffText += `<li>the event description changed</li>`;
- }
- diffText += `</ul>`;
- Event.findOneAndUpdate(
- { id: req.params.eventID },
- updatedEvent,
- function (err, raw) {
- if (err) {
- addToLog(
- "editEvent",
- "error",
- "Attempt to edit event " +
- req.params.eventID +
- " failed with error: " +
- err,
- );
- res.send(err);
- }
- },
- )
- .then(() => {
- addToLog(
- "editEvent",
- "success",
- "Event " + req.params.eventID + " edited",
- );
- // send update to ActivityPub subscribers
- Event.findOne(
- { id: req.params.eventID },
- function (err, event) {
- if (!event) return;
- let attendees = event.attendees.filter(
- (el) => el.id,
- );
- if (!err) {
- // broadcast an identical message to all followers, will show in home timeline
- const guidObject = crypto
- .randomBytes(16)
- .toString("hex");
- const jsonObject = {
- "@context":
- "https://www.w3.org/ns/activitystreams",
- id: `https://${domain}/${req.params.eventID}/m/${guidObject}`,
- name: `RSVP to ${event.name}`,
- type: "Note",
- cc: "https://www.w3.org/ns/activitystreams#Public",
- content: `${diffText} See here: <a href="https://${domain}/${req.params.eventID}">https://${domain}/${req.params.eventID}</a>`,
- };
- broadcastCreateMessage(
- jsonObject,
- event.followers,
- eventID,
- );
- // also broadcast an Update profile message to all followers so that at least Mastodon servers will update the local profile information
- const jsonUpdateObject = JSON.parse(
- event.activityPubActor,
- );
- broadcastUpdateMessage(
- jsonUpdateObject,
- event.followers,
- eventID,
- );
- // also broadcast an Update/Event for any calendar apps that are consuming our Events
- const jsonEventObject = JSON.parse(
- event.activityPubEvent,
- );
- broadcastUpdateMessage(
- jsonEventObject,
- event.followers,
- eventID,
- );
-
- // DM to attendees
- for (const attendee of attendees) {
- const jsonObject = {
- "@context":
- "https://www.w3.org/ns/activitystreams",
- name: `RSVP to ${event.name}`,
- type: "Note",
- content: `<span class=\"h-card\"><a href="${attendee.id}" class="u-url mention">@<span>${attendee.name}</span></a></span> ${diffText} See here: <a href="https://${domain}/${req.params.eventID}">https://${domain}/${req.params.eventID}</a>`,
- tag: [
- {
- type: "Mention",
- href: attendee.id,
- name: attendee.name,
- },
- ],
- };
- // send direct message to user
- sendDirectMessage(
- jsonObject,
- attendee.id,
- eventID,
- );
- }
- }
- },
- );
- // Send update to all attendees
- if (sendEmails) {
- Event.findOne({ id: req.params.eventID }).then(
- (event) => {
- const attendeeEmails = event.attendees
- .filter(
- (o) =>
- o.status === "attending" &&
- o.email,
- )
- .map((o) => o.email);
- if (attendeeEmails.length) {
- console.log(
- "Sending emails to: " +
- attendeeEmails,
- );
- req.app.get("hbsInstance").renderView(
- "./views/emails/editevent.handlebars",
- {
- diffText,
- eventID: req.params.eventID,
- siteName,
- siteLogo,
- domain,
- cache: true,
- layout: "email.handlebars",
- },
- function (err, html) {
- const msg = {
- to: attendeeEmails,
- from: {
- name: siteName,
- email: contactEmail,
- address: contactEmail,
- },
- subject: `${siteName}: ${event.name} was just edited`,
- html,
- };
- switch (mailService) {
- case "sendgrid":
- sgMail
- .sendMultiple(msg)
- .catch((e) => {
- console.error(
- e.toString(),
- );
- res.status(
- 500,
- ).end();
- });
- break;
- case "nodemailer":
- nodemailerTransporter
- .sendMail(msg)
- .catch((e) => {
- console.error(
- e.toString(),
- );
- res.status(
- 500,
- ).end();
- });
- break;
- }
- },
- );
- } else {
- console.log("Nothing to send!");
- }
- },
- );
- }
- res.writeHead(302, {
- Location:
- "/" +
- req.params.eventID +
- "?e=" +
- req.params.editToken,
- });
- res.end();
- })
- .catch((err) => {
- console.error(err);
- res.send("Sorry! Something went wrong!");
- addToLog(
- "editEvent",
- "error",
- "Attempt to edit event " +
- req.params.eventID +
- " failed with error: " +
- err,
- );
- });
- } else {
- // Token doesn't match
- res.send("Sorry! Something went wrong");
- addToLog(
- "editEvent",
- "error",
- "Attempt to edit event " +
- req.params.eventID +
- " failed with error: token does not match",
- );
- }
- })
- .catch((err) => {
- console.error(err);
- res.send("Sorry! Something went wrong!");
- addToLog(
- "editEvent",
- "error",
- "Attempt to edit event " +
- req.params.eventID +
- " failed with error: " +
- err,
- );
- });
-});
-
router.post("/editeventgroup/:eventGroupID/:editToken", (req, res) => {
let submittedEditToken = req.params.editToken;
EventGroup.findOne({
@@ -1506,6 +754,7 @@ router.post("/editeventgroup/:eventGroupID/:editToken", (req, res) => {
router.post("/deleteimage/:eventID/:editToken", (req, res) => {
let submittedEditToken = req.params.editToken;
+ let eventImage;
Event.findOne({
id: req.params.eventID,
}).then((event) => {