summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/app.ts5
-rwxr-xr-xsrc/routes.js197
-rw-r--r--src/routes/event.ts5
-rw-r--r--src/routes/frontend.ts37
-rw-r--r--src/routes/group.ts240
-rw-r--r--src/util/validation.ts51
6 files changed, 321 insertions, 214 deletions
diff --git a/src/app.ts b/src/app.ts
index 30cf02d..c43f31d 100755
--- a/src/app.ts
+++ b/src/app.ts
@@ -5,6 +5,7 @@ import routes from "./routes.js";
import frontend from "./routes/frontend.js";
import activitypub from "./routes/activitypub.js";
import event from "./routes/event.js";
+import group from "./routes/group.js";
import { initEmailService } from "./lib/email.js";
@@ -33,6 +34,9 @@ const hbsInstance = hbs.create({
match[1] + (match[3] || "s")
); // Plural case: 'bagel(s)' or 'bagel' --> bagels
},
+ json: function (context: any) {
+ return JSON.stringify(context);
+ },
},
});
app.engine("handlebars", hbsInstance.engine);
@@ -52,6 +56,7 @@ app.use(express.urlencoded({ extended: true }));
app.use("/", frontend);
app.use("/", activitypub);
app.use("/", event);
+app.use("/", group);
app.use("/", routes);
export default app;
diff --git a/src/routes.js b/src/routes.js
index e4ef3cb..96420c7 100755
--- a/src/routes.js
+++ b/src/routes.js
@@ -298,102 +298,6 @@ router.post("/importevent", (req, res) => {
}
});
-router.post("/neweventgroup", (req, res) => {
- let eventGroupID = nanoid();
- let editToken = randomstring.generate();
- let eventGroupImageFilename = "";
- if (req.files && Object.keys(req.files).length !== 0) {
- let eventImageBuffer = req.files.imageUpload.data;
- Jimp.read(eventImageBuffer, (err, img) => {
- if (err)
- addToLog(
- "Jimp",
- "error",
- "Attempt to edit image failed with error: " + err,
- );
- img.resize(920, Jimp.AUTO) // resize
- .quality(80) // set JPEG quality
- .write("./public/events/" + eventGroupID + ".jpg"); // save
- });
- eventGroupImageFilename = eventGroupID + ".jpg";
- }
- const eventGroup = new EventGroup({
- id: eventGroupID,
- name: req.body.eventGroupName,
- description: req.body.eventGroupDescription,
- image: eventGroupImageFilename,
- creatorEmail: req.body.creatorEmail,
- url: req.body.eventGroupURL,
- hostName: req.body.hostName,
- editToken: editToken,
- firstLoad: true,
- });
- eventGroup
- .save()
- .then(() => {
- addToLog(
- "createEventGroup",
- "success",
- "Event group " + eventGroupID + " created",
- );
- // Send email with edit link
- if (req.body.creatorEmail && sendEmails) {
- req.app.get("hbsInstance").renderView(
- "./views/emails/createeventgroup.handlebars",
- {
- eventGroupID,
- 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.eventGroupName}`,
- 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: "/group/" + eventGroupID + "?e=" + editToken,
- });
- res.end();
- })
- .catch((err) => {
- res.send("Database error, please try again :( - " + err);
- addToLog(
- "createEvent",
- "error",
- "Attempt to create event failed with error: " + err,
- );
- });
-});
-
router.post("/verifytoken/event/:eventID", (req, res) => {
Event.findOne({
id: req.params.eventID,
@@ -414,107 +318,6 @@ router.post("/verifytoken/group/:eventGroupID", (req, res) => {
});
});
-router.post("/editeventgroup/:eventGroupID/:editToken", (req, res) => {
- let submittedEditToken = req.params.editToken;
- EventGroup.findOne({
- id: req.params.eventGroupID,
- })
- .then((eventGroup) => {
- if (eventGroup.editToken === submittedEditToken) {
- // Token matches
-
- // If there is a new image, upload that first
- let eventGroupID = req.params.eventGroupID;
- let eventGroupImageFilename = eventGroup.image;
- if (req.files && Object.keys(req.files).length !== 0) {
- let eventImageBuffer = req.files.eventGroupImageUpload.data;
- Jimp.read(eventImageBuffer, (err, img) => {
- if (err) throw err;
- img.resize(920, Jimp.AUTO) // resize
- .quality(80) // set JPEG
- .write("./public/events/" + eventGroupID + ".jpg"); // save
- });
- eventGroupImageFilename = eventGroupID + ".jpg";
- }
- const updatedEventGroup = {
- name: req.body.eventGroupName,
- description: req.body.eventGroupDescription,
- url: req.body.eventGroupURL,
- hostName: req.body.hostName,
- image: eventGroupImageFilename,
- };
- EventGroup.findOneAndUpdate(
- { id: req.params.eventGroupID },
- updatedEventGroup,
- function (err, raw) {
- if (err) {
- addToLog(
- "editEventGroup",
- "error",
- "Attempt to edit event group " +
- req.params.eventGroupID +
- " failed with error: " +
- err,
- );
- res.send(err);
- }
- },
- )
- .then(() => {
- addToLog(
- "editEventGroup",
- "success",
- "Event group " +
- req.params.eventGroupID +
- " edited",
- );
- res.writeHead(302, {
- Location:
- "/group/" +
- req.params.eventGroupID +
- "?e=" +
- req.params.editToken,
- });
- res.end();
- })
- .catch((err) => {
- console.error(err);
- res.send("Sorry! Something went wrong!");
- addToLog(
- "editEventGroup",
- "error",
- "Attempt to edit event group " +
- req.params.eventGroupID +
- " failed with error: " +
- err,
- );
- });
- } else {
- // Token doesn't match
- res.send("Sorry! Something went wrong");
- addToLog(
- "editEventGroup",
- "error",
- "Attempt to edit event group " +
- req.params.eventGroupID +
- " failed with error: token does not match",
- );
- }
- })
- .catch((err) => {
- console.error(err);
- res.send("Sorry! Something went wrong!");
- addToLog(
- "editEventGroup",
- "error",
- "Attempt to edit event group " +
- req.params.eventGroupID +
- " failed with error: " +
- err,
- );
- });
-});
-
router.post("/deleteimage/:eventID/:editToken", (req, res) => {
let submittedEditToken = req.params.editToken;
let eventImage;
diff --git a/src/routes/event.ts b/src/routes/event.ts
index 375871b..be27fd4 100644
--- a/src/routes/event.ts
+++ b/src/routes/event.ts
@@ -74,8 +74,7 @@ router.post(
img.resize(920, Jimp.AUTO) // resize
.quality(80) // set JPEG quality
.write("./public/events/" + eventID + ".jpg"); // save
- const filename = eventID + ".jpg";
- return filename;
+ return eventID + ".jpg";
})
.catch((err) => {
addToLog(
@@ -280,8 +279,8 @@ router.put(
});
}
- let submittedEditToken = req.body.editToken;
try {
+ const submittedEditToken = req.body.editToken;
const event = await Event.findOne({
id: req.params.eventID,
});
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(
diff --git a/src/routes/group.ts b/src/routes/group.ts
new file mode 100644
index 0000000..2801248
--- /dev/null
+++ b/src/routes/group.ts
@@ -0,0 +1,240 @@
+import { Router, Response, Request } from "express";
+import getConfig from "../lib/config.js";
+import multer from "multer";
+import { generateEditToken, generateEventID } from "../util/generator.js";
+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";
+
+const config = getConfig();
+
+const storage = multer.memoryStorage();
+// Accept only JPEG, GIF or PNG images, up to 10MB
+const upload = multer({
+ storage: storage,
+ limits: { fileSize: 10 * 1024 * 1024 },
+ fileFilter: function (_, file, cb) {
+ const filetypes = /jpeg|jpg|png|gif/;
+ const mimetype = filetypes.test(file.mimetype);
+ if (!mimetype) {
+ return cb(new Error("Only JPEG, PNG and GIF images are allowed."));
+ }
+ cb(null, true);
+ },
+});
+
+const router = Router();
+
+router.post(
+ "/group",
+ upload.single("imageUpload"),
+ async (req: Request, res: Response) => {
+ const { data: groupData, errors } = validateGroupData(req.body);
+ if (errors && errors.length > 0) {
+ return res.status(400).json({ errors });
+ }
+ if (!groupData) {
+ return res.status(400).json({
+ errors: [
+ {
+ message: "No group data was provided.",
+ },
+ ],
+ });
+ }
+
+ try {
+ const groupID = generateEventID();
+ const editToken = generateEditToken();
+ let groupImageFilename;
+
+ if (req.file?.buffer) {
+ groupImageFilename = await Jimp.read(req.file.buffer)
+ .then((img) => {
+ img.resize(920, Jimp.AUTO) // resize
+ .quality(80) // set JPEG quality
+ .write("./public/events/" + groupID + ".jpg"); // save
+ return groupID + ".jpg";
+ })
+ .catch((err) => {
+ addToLog(
+ "Jimp",
+ "error",
+ "Attempt to edit image failed with error: " + err,
+ );
+ });
+ }
+
+ const eventGroup = new EventGroup({
+ id: groupID,
+ name: groupData.eventGroupName,
+ description: groupData.eventGroupDescription,
+ image: groupImageFilename,
+ creatorEmail: groupData.creatorEmail,
+ url: groupData.eventGroupURL,
+ hostName: groupData.hostName,
+ editToken: editToken,
+ firstLoad: true,
+ });
+
+ await eventGroup.save();
+
+ addToLog(
+ "createEventGroup",
+ "success",
+ "Event group " + groupID + " created",
+ );
+
+ // Send email with edit link
+ if (groupData.creatorEmail && req.app.locals.sendEmails) {
+ sendEmailFromTemplate(
+ groupData.creatorEmail,
+ `${eventGroup.name}`,
+ "createEventGroup",
+ {
+ eventGroupID: eventGroup.id,
+ editToken: eventGroup.editToken,
+ siteName: config.general.site_name,
+ siteLogo: config.general.email_logo_url,
+ domain: config.general.domain,
+ },
+ req,
+ );
+ }
+
+ res.status(200).json({
+ id: groupID,
+ editToken: editToken,
+ url: `/group/${groupID}?e=${editToken}`,
+ });
+ } catch (err) {
+ console.error(err);
+ addToLog(
+ "createEvent",
+ "error",
+ "Attempt to create event failed with error: " + err,
+ );
+ return res.status(500).json({
+ errors: [
+ {
+ message: err,
+ },
+ ],
+ });
+ }
+ },
+);
+
+router.put(
+ "/group/:eventGroupID",
+ upload.single("imageUpload"),
+ async (req: Request, res: Response) => {
+ const { data: groupData, errors } = validateGroupData(req.body);
+ if (errors && errors.length > 0) {
+ return res.status(400).json({ errors });
+ }
+ if (!groupData) {
+ return res.status(400).json({
+ errors: [
+ {
+ message: "No group data was provided.",
+ },
+ ],
+ });
+ }
+
+ try {
+ const submittedEditToken = req.body.editToken;
+ const eventGroup = await EventGroup.findOne({
+ id: req.params.eventGroupID,
+ });
+ if (!eventGroup) {
+ return res.status(404).json({
+ errors: [
+ {
+ message: "Event group not found.",
+ },
+ ],
+ });
+ }
+
+ if (eventGroup.editToken !== submittedEditToken) {
+ // Token doesn't match
+ addToLog(
+ "editEventGroup",
+ "error",
+ `Attempt to edit event group ${req.params.eventGroupID} failed with error: token does not match`,
+ );
+ return res.status(403).json({
+ errors: [
+ {
+ message: "Edit token is invalid.",
+ },
+ ],
+ });
+ }
+ // Token matches
+ // If there is a new image, upload that first
+ let eventGroupID = req.params.eventGroupID;
+ let eventGroupImageFilename = eventGroup.image;
+ if (req.file?.buffer) {
+ Jimp.read(req.file.buffer)
+ .then((img) => {
+ img.resize(920, Jimp.AUTO) // resize
+ .quality(80) // set JPEG quality
+ .write(`./public/events/${eventGroupID}.jpg`); // save
+ })
+ .catch((err) => {
+ addToLog(
+ "Jimp",
+ "error",
+ "Attempt to edit image failed with error: " + err,
+ );
+ });
+ eventGroupImageFilename = eventGroupID + ".jpg";
+ }
+
+ const updatedEventGroup = {
+ name: req.body.eventGroupName,
+ description: req.body.eventGroupDescription,
+ url: req.body.eventGroupURL,
+ hostName: req.body.hostName,
+ image: eventGroupImageFilename,
+ };
+
+ await EventGroup.findOneAndUpdate(
+ { id: req.params.eventGroupID },
+ updatedEventGroup,
+ );
+
+ addToLog(
+ "editEventGroup",
+ "success",
+ "Event group " + req.params.eventGroupID + " edited",
+ );
+
+ res.sendStatus(200);
+ } catch (err) {
+ console.error(err);
+ addToLog(
+ "editEventGroup",
+ "error",
+ "Attempt to edit event group " +
+ req.params.eventGroupID +
+ " failed with error: " +
+ err,
+ );
+ return res.status(500).json({
+ errors: [
+ {
+ message: err,
+ },
+ ],
+ });
+ }
+ },
+);
+
+export default router;
diff --git a/src/util/validation.ts b/src/util/validation.ts
index f51769e..732fbf3 100644
--- a/src/util/validation.ts
+++ b/src/util/validation.ts
@@ -44,6 +44,14 @@ export type ValidatedEventData = Omit<
maxAttendeesBoolean: boolean;
};
+interface EventGroupData {
+ eventGroupName: string;
+ eventGroupDescription: string;
+ eventGroupURL: string;
+ hostName: string;
+ creatorEmail: string;
+}
+
const validateEmail = (email: string) => {
if (!email || email.length === 0 || typeof email !== "string") {
return false;
@@ -83,23 +91,11 @@ export const validateEventTime = (start: Date, end: Date): Error | boolean => {
export const validateEventData = (eventData: EventData): ValidationResponse => {
const validatedData: ValidatedEventData = {
- eventName: eventData.eventName,
- eventLocation: eventData.eventLocation,
- eventStart: eventData.eventStart,
- eventEnd: eventData.eventEnd,
- timezone: eventData.timezone,
- eventDescription: eventData.eventDescription,
- eventURL: eventData.eventURL,
- imagePath: eventData.imagePath,
- hostName: eventData.hostName,
- creatorEmail: eventData.creatorEmail,
+ ...eventData,
eventGroupBoolean: eventData.eventGroupCheckbox === "true",
interactionBoolean: eventData.interactionCheckbox === "true",
joinBoolean: eventData.joinCheckbox === "true",
maxAttendeesBoolean: eventData.maxAttendeesCheckbox === "true",
- eventGroupID: eventData.eventGroupID,
- eventGroupEditToken: eventData.eventGroupEditToken,
- maxAttendees: eventData.maxAttendees,
};
const errors: Error[] = [];
if (!validatedData.eventName) {
@@ -189,3 +185,32 @@ export const validateEventData = (eventData: EventData): ValidationResponse => {
errors: errors,
};
};
+
+export const validateGroupData = (groupData: EventGroupData) => {
+ const errors: Error[] = [];
+ if (!groupData.eventGroupName) {
+ errors.push({
+ message: "Event group name is required.",
+ field: "eventGroupName",
+ });
+ }
+ if (!groupData.eventGroupDescription) {
+ errors.push({
+ message: "Event group description is required.",
+ field: "eventGroupDescription",
+ });
+ }
+ if (groupData.creatorEmail) {
+ if (!validateEmail(groupData.creatorEmail)) {
+ errors.push({
+ message: "Email address is invalid.",
+ field: "creatorEmail",
+ });
+ }
+ }
+
+ return {
+ data: groupData,
+ errors: errors,
+ };
+};