summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/config.ts2
-rw-r--r--src/util/generator.ts24
-rw-r--r--src/util/validation.ts191
3 files changed, 217 insertions, 0 deletions
diff --git a/src/util/config.ts b/src/util/config.ts
index c65fdb0..d1fd05b 100644
--- a/src/util/config.ts
+++ b/src/util/config.ts
@@ -7,6 +7,7 @@ interface FrontendConfig {
email: string;
siteName: string;
showKofi: boolean;
+ isFederated: boolean;
}
export const frontendConfig = (): FrontendConfig => ({
@@ -14,4 +15,5 @@ export const frontendConfig = (): FrontendConfig => ({
email: config.general.email,
siteName: config.general.site_name,
showKofi: config.general.show_kofi,
+ isFederated: config.general.is_federated,
});
diff --git a/src/util/generator.ts b/src/util/generator.ts
new file mode 100644
index 0000000..c3712c1
--- /dev/null
+++ b/src/util/generator.ts
@@ -0,0 +1,24 @@
+import crypto from "crypto";
+
+const generateAlphanumericString = (length: number) => {
+ return Array(length)
+ .fill(0)
+ .map((x) => Math.random().toString(36).charAt(2))
+ .join("");
+};
+
+export const generateEditToken = () => generateAlphanumericString(32);
+
+export const generateRSAKeypair = () => {
+ return crypto.generateKeyPairSync("rsa", {
+ modulusLength: 4096,
+ publicKeyEncoding: {
+ type: "spki",
+ format: "pem",
+ },
+ privateKeyEncoding: {
+ type: "pkcs8",
+ format: "pem",
+ },
+ });
+};
diff --git a/src/util/validation.ts b/src/util/validation.ts
new file mode 100644
index 0000000..f51769e
--- /dev/null
+++ b/src/util/validation.ts
@@ -0,0 +1,191 @@
+import moment from "moment-timezone";
+
+type Error = {
+ message?: string;
+ field?: string;
+};
+
+type ValidationResponse = {
+ data?: ValidatedEventData;
+ errors?: Error[];
+};
+
+interface EventData {
+ eventName: string;
+ eventLocation: string;
+ eventStart: string;
+ eventEnd: string;
+ timezone: string;
+ eventDescription: string;
+ eventURL: string;
+ imagePath: string;
+ hostName: string;
+ creatorEmail: string;
+ eventGroupCheckbox: string;
+ eventGroupID: string;
+ eventGroupEditToken: string;
+ interactionCheckbox: string;
+ joinCheckbox: string;
+ maxAttendeesCheckbox: string;
+ maxAttendees: number;
+}
+
+// EventData without the 'checkbox' fields
+export type ValidatedEventData = Omit<
+ EventData,
+ | "eventGroupCheckbox"
+ | "interactionCheckbox"
+ | "joinCheckbox"
+ | "maxAttendeesCheckbox"
+> & {
+ eventGroupBoolean: boolean;
+ interactionBoolean: boolean;
+ joinBoolean: boolean;
+ maxAttendeesBoolean: boolean;
+};
+
+const validateEmail = (email: string) => {
+ if (!email || email.length === 0 || typeof email !== "string") {
+ return false;
+ }
+ var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return re.test(email);
+};
+
+export const validateEventTime = (start: Date, end: Date): Error | boolean => {
+ if (moment(start).isAfter(moment(end))) {
+ return {
+ message: "Start time must be before end time.",
+ field: "eventStart",
+ };
+ }
+ if (moment(start).isBefore(moment())) {
+ return {
+ message: "Start time must be in the future.",
+ field: "eventStart",
+ };
+ }
+ if (moment(end).isBefore(moment())) {
+ return {
+ message: "End time must be in the future.",
+ field: "eventEnd",
+ };
+ }
+ // Duration cannot be longer than 1 year
+ if (moment(end).diff(moment(start), "years") > 1) {
+ return {
+ message: "Event duration cannot be longer than 1 year.",
+ field: "eventEnd",
+ };
+ }
+ return true;
+};
+
+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,
+ 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) {
+ errors.push({
+ message: "Event name is required.",
+ field: "eventName",
+ });
+ }
+ if (!validatedData.eventLocation) {
+ errors.push({
+ message: "Event location is required.",
+ field: "eventLocation",
+ });
+ }
+ if (!validatedData.eventStart) {
+ errors.push({
+ message: "Event start time is required.",
+ field: "eventStart",
+ });
+ }
+ if (!validatedData.eventEnd) {
+ errors.push({
+ message: "Event end time is required.",
+ field: "eventEnd",
+ });
+ }
+ const timeValidation = validateEventTime(
+ new Date(validatedData.eventStart),
+ new Date(validatedData.eventEnd),
+ );
+ if (timeValidation !== true && timeValidation !== false) {
+ errors.push({
+ message: timeValidation.message,
+ });
+ }
+ if (!validatedData.timezone) {
+ errors.push({
+ message: "Event timezone is required.",
+ field: "timezone",
+ });
+ }
+ if (!validatedData.eventDescription) {
+ errors.push({
+ message: "Event description is required.",
+ field: "eventDescription",
+ });
+ }
+ if (validatedData.eventGroupBoolean) {
+ if (!validatedData.eventGroupID) {
+ errors.push({
+ message: "Event group ID is required.",
+ field: "eventGroupID",
+ });
+ }
+ if (!validatedData.eventGroupEditToken) {
+ errors.push({
+ message: "Event group edit token is required.",
+ field: "eventGroupEditToken",
+ });
+ }
+ }
+ if (validatedData.maxAttendeesBoolean) {
+ if (!validatedData.maxAttendees) {
+ errors.push({
+ message: "Max number of attendees is required.",
+ field: "maxAttendees",
+ });
+ }
+ if (isNaN(validatedData.maxAttendees)) {
+ errors.push({
+ message: "Max number of attendees must be a number.",
+ field: "maxAttendees",
+ });
+ }
+ }
+ if (validatedData.creatorEmail) {
+ if (!validateEmail(validatedData.creatorEmail)) {
+ errors.push({
+ message: "Email address is invalid.",
+ field: "creatorEmail",
+ });
+ }
+ }
+
+ return {
+ data: validatedData,
+ errors: errors,
+ };
+};