Skip to content

Commit

Permalink
Refactor: import event form
Browse files Browse the repository at this point in the history
  • Loading branch information
lowercasename committed Oct 8, 2023
1 parent 6b220e0 commit 6ab556e
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 120 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
},
"devDependencies": {
"@types/express": "^4.17.18",
"@types/ical": "^0.8.1",
"@types/multer": "^1.4.8",
"@types/node": "^20.8.2",
"@types/nodemailer": "^6.4.11",
Expand Down
22 changes: 21 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions public/js/modules/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,47 @@ function newEventGroupForm() {
},
};
}

function importEventForm() {
return {
data: {
creatorEmail: "",
},
errors: [],
submitting: false,
async submitForm() {
this.submitting = true;
this.errors = [];
const formData = new FormData();
for (const [key, value] of Object.entries(this.data)) {
formData.append(key, value);
}
formData.append(
"icsImportControl",
this.$refs.icsImportControl.files[0],
);
try {
const response = await fetch("/import/event", {
method: "POST",
body: formData,
});
this.submitting = false;
if (!response.ok) {
if (response.status !== 400) {
this.errors = unexpectedError;
return;
}
const json = await response.json();
this.errors = json.errors;
return;
}
const json = await response.json();
window.location.assign(json.url);
} catch (error) {
console.log(error);
this.errors = unexpectedError;
this.submitting = false;
}
},
};
}
115 changes: 0 additions & 115 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,121 +183,6 @@ schedule.scheduleJob("59 23 * * *", function (fireDate) {
});

// BACKEND ROUTES
router.post("/importevent", (req, res) => {
let eventID = nanoid();
let editToken = randomstring.generate();
if (req.files && Object.keys(req.files).length !== 0) {
let iCalObject = ical.parseICS(
req.files.icsImportControl.data.toString("utf8"),
);
let importedEventData = iCalObject[Object.keys(iCalObject)];

let creatorEmail;
if (req.body.creatorEmail) {
creatorEmail = req.body.creatorEmail;
} else if (importedEventData.organizer) {
creatorEmail = importedEventData.organizer.val.replace(
"MAILTO:",
"",
);
}

const event = new Event({
id: eventID,
type: "public",
name: importedEventData.summary,
location: importedEventData.location,
start: importedEventData.start,
end: importedEventData.end,
timezone:
typeof importedEventData.start.tz !== "undefined"
? importedEventData.start.tz
: "Etc/UTC",
description: importedEventData.description,
image: "",
creatorEmail: creatorEmail,
url: "",
hostName: importedEventData.organizer
? importedEventData.organizer.params.CN.replace(/["]+/g, "")
: "",
viewPassword: "",
editPassword: "",
editToken: editToken,
usersCanAttend: false,
showUsersList: false,
usersCanComment: false,
firstLoad: true,
});
event
.save()
.then(() => {
addToLog(
"createEvent",
"success",
"Event " + eventID + " created",
);
// Send email with edit link
if (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}: ${importedEventData.summary}`,
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) => {
res.send("Database error, please try again :(");
addToLog(
"createEvent",
"error",
"Attempt to create event failed with error: " + err,
);
});
} else {
console.log("Files array is empty!");
res.status(500).end();
}
});

router.post("/verifytoken/event/:eventID", (req, res) => {
Event.findOne({
id: req.params.eventID,
Expand Down
126 changes: 126 additions & 0 deletions src/routes/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import getConfig from "../lib/config.js";
import { sendEmailFromTemplate } from "../lib/email.js";
import crypto from "crypto";
import ical from "ical";

const config = getConfig();

Expand All @@ -42,6 +43,17 @@ const upload = multer({
cb(null, true);
},
});
const icsUpload = multer({
storage: storage,
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: function (_, file, cb) {
const filetype = "text/calendar";
if (file.mimetype !== filetype) {
return cb(new Error("Only ICS files are allowed."));
}
cb(null, true);
},
});

const router = Router();

Expand Down Expand Up @@ -84,6 +96,7 @@ router.post(
);
});
}

const startUTC = moment.tz(eventData.eventStart, eventData.timezone);
const endUTC = moment.tz(eventData.eventEnd, eventData.timezone);
let eventGroup;
Expand Down Expand Up @@ -511,4 +524,117 @@ router.put(
},
);

router.post(
"/import/event",
icsUpload.single("icsImportControl"),
async (req: Request, res: Response) => {
if (!req.file) {
return res.status(400).json({
errors: [
{
message: "No file was provided.",
},
],
});
}

let eventID = generateEventID();
let editToken = generateEditToken();

let iCalObject = ical.parseICS(req.file.buffer.toString("utf8"));

let importedEventData = iCalObject[Object.keys(iCalObject)[0]];

let creatorEmail: string | undefined;
if (req.body.creatorEmail) {
creatorEmail = req.body.creatorEmail;
} else if (importedEventData.organizer) {
if (typeof importedEventData.organizer === "string") {
creatorEmail = importedEventData.organizer.replace(
"MAILTO:",
"",
);
} else {
creatorEmail = importedEventData.organizer.val.replace(
"MAILTO:",
"",
);
}
}

let hostName: string | undefined;
if (importedEventData.organizer) {
if (typeof importedEventData.organizer === "string") {
hostName = importedEventData.organizer.replace(/["]+/g, "");
} else {
hostName = importedEventData.organizer.params.CN.replace(
/["]+/g,
"",
);
}
}

const event = new Event({
id: eventID,
type: "public",
name: importedEventData.summary,
location: importedEventData.location,
start: importedEventData.start,
end: importedEventData.end,
timezone: "Etc/UTC", // TODO: get timezone from ics file
description: importedEventData.description,
image: "",
creatorEmail,
url: "",
hostName,
viewPassword: "",
editPassword: "",
editToken: editToken,
usersCanAttend: false,
showUsersList: false,
usersCanComment: false,
firstLoad: true,
});
try {
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",
{
eventID,
editToken,
siteName: config.general.site_name,
siteLogo: config.general.email_logo_url,
domain: config.general.domain,
},
req,
);
}
return res.json({
eventID: eventID,
editToken: editToken,
url: `/${eventID}?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,
},
],
});
}
},
);

export default router;
Loading

0 comments on commit 6ab556e

Please sign in to comment.