Skip to content

Commit

Permalink
Merge branch 'main' into dev/jacob/partial-attendee-put-2
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobc2700 authored Jul 20, 2024
2 parents 935fe2e + 971dc28 commit 825f136
Show file tree
Hide file tree
Showing 10 changed files with 945 additions and 65 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.496.0",
"@aws-sdk/client-ses": "^3.609.0",
"@aws-sdk/s3-presigned-post": "^3.499.0",
"@aws-sdk/s3-request-presigner": "^3.496.0",
"@paralleldrive/cuid2": "^2.2.2",
Expand Down
9 changes: 7 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { StatusCodes } from "http-status-codes";
import { Config } from "./config";
import { rateLimiter } from "./middleware/rateLimiter";
import { isTest } from "./utilities";
import AWS from "aws-sdk";

import databaseMiddleware from "./middleware/database-middleware";
import customCors from "./middleware/cors-middleware";
Expand All @@ -18,10 +19,15 @@ import notificationsRouter from "./services/notifications/notifications-router";
import registrationRouter from "./services/registration/registration-router";
import s3Router from "./services/s3/s3-router";
import statsRouter from "./services/stats/stats-router";
import sponsorRouter from "./services/sponsor/sponsor-router";
import subscriptionRouter from "./services/subscription/subscription-router";
import speakersRouter from "./services/speakers/speakers-router";

AWS.config.update({
region: Config.S3_REGION,
accessKeyId: Config.S3_ACCESS_KEY,
secretAccessKey: Config.S3_SECRET_KEY,
});

const app = express();

// to prevent server-side caching/returning status code 200
Expand All @@ -46,7 +52,6 @@ app.use("/notifications", databaseMiddleware, notificationsRouter);
app.use("/registration", databaseMiddleware, registrationRouter);
app.use("/s3", databaseMiddleware, s3Router);
app.use("/stats", databaseMiddleware, statsRouter);
app.use("/sponsor", databaseMiddleware, sponsorRouter);
app.use("/subscription", databaseMiddleware, subscriptionRouter);
app.use("/speakers", databaseMiddleware, speakersRouter);

Expand Down
8 changes: 8 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import dotenv from "dotenv";
import { z } from "zod";
import { getEnv } from "./utilities";

import AWS from "aws-sdk";

dotenv.config();

export const Environment = z.enum(["PRODUCTION", "DEVELOPMENT", "TESTING"]);
Expand Down Expand Up @@ -79,6 +81,8 @@ export const Config = {
// QR Scanning
QR_HASH_ITERATIONS: 10000,
QR_HASH_SECRET: getEnv("QR_HASH_SECRET"),

OUTGOING_EMAIL_ADDRESSES: z.enum(["[email protected]"]),
};

export const DeviceRedirects: Record<string, string> = {
Expand All @@ -87,4 +91,8 @@ export const DeviceRedirects: Record<string, string> = {
mobile: "exp://192.168.86.24:8081/--/Main",
};

export const ses = new AWS.SES({
region: Config.S3_REGION,
});

export default Config;
33 changes: 17 additions & 16 deletions src/services/attendee/attendee-router.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { Router } from "express";
import { StatusCodes } from "http-status-codes";
import {
AttendeeValidator,
EventIdValidator,
PartialAttendeeValidator,
} from "./attendee-schema";
import { AttendeeValidator, EventIdValidator } from "./attendee-schema";
import { Database } from "../../database";
import RoleChecker from "../../middleware/role-checker";
import { Role } from "../auth/auth-models";
Expand Down Expand Up @@ -131,7 +127,7 @@ attendeeRouter.get(
);

attendeeRouter.get(
"/",
"/points",
RoleChecker([Role.Enum.USER]),
async (req, res, next) => {
try {
Expand All @@ -147,26 +143,31 @@ attendeeRouter.get(
.json({ error: "UserNotFound" });
}

return res.status(StatusCodes.OK).json(user);
return res.status(StatusCodes.OK).json({ points: user.points });
} catch (error) {
next(error);
}
}
);

// Get attendees based on a partial filter in body
attendeeRouter.get(
"/filter",
RoleChecker([Role.Enum.STAFF, Role.Enum.CORPORATE]),
"/",
RoleChecker([Role.Enum.USER]),
async (req, res, next) => {
try {
const attendeeData = PartialAttendeeValidator.parse(req.body);
const attendees = await Database.ATTENDEE.find(
attendeeData,
"userId"
);
const payload = res.locals.payload;
const userId = payload.userId;

return res.status(StatusCodes.OK).json(attendees);
// Check if the user exists in the database
const user = await Database.ATTENDEE.findOne({ userId });

if (!user) {
return res
.status(StatusCodes.NOT_FOUND)
.json({ error: "UserNotFound" });
}

return res.status(StatusCodes.OK).json(user);
} catch (error) {
next(error);
}
Expand Down
5 changes: 5 additions & 0 deletions src/services/events/events-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const publicEventValidator = z.object({
description: z.string(),
isVirtual: z.boolean(),
imageUrl: z.string().nullable().optional(),
location: z.string().nullable().optional(),
eventType: EventType,
});

Expand Down Expand Up @@ -56,6 +57,10 @@ export const EventSchema = new Schema({
type: String,
default: null,
},
location: {
type: String,
default: null,
},
isVisible: {
type: Boolean,
default: false,
Expand Down
24 changes: 23 additions & 1 deletion src/services/registration/registration-router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Router } from "express";
import { StatusCodes } from "http-status-codes";
import { RegistrationValidator } from "./registration-schema";
import {
RegistrationFilterValidator,
RegistrationValidator,
} from "./registration-schema";
import { Database } from "../../database";
import RoleChecker from "../../middleware/role-checker";
import { Role } from "../auth/auth-models";
Expand Down Expand Up @@ -107,4 +110,23 @@ registrationRouter.get("/", RoleChecker([]), async (req, res, next) => {
}
});

// Get attendees based on a partial filter in body
registrationRouter.get(
"/filter",
RoleChecker([Role.Enum.STAFF, Role.Enum.CORPORATE]),
async (req, res, next) => {
try {
const filterData = RegistrationFilterValidator.parse(req.body);
const projection = Object.assign({}, ...filterData.projection);
const attendees = await Database.REGISTRATION.find(
filterData.filter,
{ ...projection, hasSubmitted: 1 }
);
return res.status(StatusCodes.OK).json(attendees);
} catch (error) {
next(error);
}
}
);

export default registrationRouter;
18 changes: 16 additions & 2 deletions src/services/registration/registration-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const RegistrationValidator = z.object({
jobInterest: z.array(z.string()).nullable().optional(),
isInterestedMechMania: z.boolean(),
isInterestedPuzzleBang: z.boolean(),
hasResume: z.boolean().optional(),
hasResume: z.boolean().default(false),
hasSubmitted: z.boolean().optional(),
});

Expand Down Expand Up @@ -49,4 +49,18 @@ const RegistrationSchema = new mongoose.Schema({
hasSubmitted: { type: Boolean, default: false },
});

export { RegistrationSchema, RegistrationValidator };
// Partial schema for attendee filter
const PartialRegistrationValidator = RegistrationValidator.partial();

const RegistrationFilterValidator = z.object({
filter: PartialRegistrationValidator,
projection: z.array(
z.record(PartialRegistrationValidator.keyof(), z.number().min(1).max(1))
),
});

export {
RegistrationSchema,
RegistrationValidator,
RegistrationFilterValidator,
};
40 changes: 40 additions & 0 deletions src/services/ses/ses-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ses, Config } from "../../config";

export function sendManyEmails(
emailIds: string[],
subject: string,
emailBody: string
): Promise<AWS.SES.SendEmailResponse>[] {
const emailPromises: Promise<AWS.SES.SendEmailResponse>[] = [];
for (let i = 0; i < emailIds.length; i++) {
emailPromises.push(sendEmail(emailIds[i], subject, emailBody));
}
return emailPromises;
}

export function sendEmail(
emailId: string,
subject: string,
emailBody: string
): Promise<AWS.SES.SendEmailResponse> {
return ses
.sendEmail({
Destination: {
ToAddresses: [emailId],
},
Message: {
Body: {
Text: {
Data: emailBody,
},
},
Subject: {
Data: subject,
},
},
Source: Config.OUTGOING_EMAIL_ADDRESSES.Enum[
"[email protected]"
],
})
.promise();
}
33 changes: 0 additions & 33 deletions src/services/sponsor/sponsor-router.ts

This file was deleted.

Loading

0 comments on commit 825f136

Please sign in to comment.