Skip to content

Commit

Permalink
Merge branch 'main' into dev/jacob/authroutes
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobc2700 authored Jun 9, 2024
2 parents 0210938 + a4e70a5 commit 74c8f8e
Show file tree
Hide file tree
Showing 16 changed files with 1,782 additions and 8,695 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# API for Reflections | Projections 2024

Test

Contributors:

- Aydan Pirani
Expand Down
7,201 changes: 0 additions & 7,201 deletions package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash
cd /home/ubuntu/rp-api
yarn build
yarn build
10 changes: 9 additions & 1 deletion scripts/install_dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#!/bin/bash
cd /home/ubuntu/rp-api
sudo yarn
sudo yarn

sudo pm2 describe appname 2>&1 /dev/null
RUNNING=$?


if [ "${RUNNING}" -eq 0 ]; then
sudo pm2 start build/app.js --name RP_API -i 2 --wait-ready --listen-timeout 10000
fi;
4 changes: 2 additions & 2 deletions scripts/reload_server.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash
udo pm2 reload RP_API!/bin/bash
cd /home/ubuntu/rp-api
sudo pm2 reload RP_API
sudo pm2 reload RP_API
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import eventsRouter from "./services/events/events-router";
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 subscriptionRouter from "./services/subscription/subscription-router";

const app = express();
Expand All @@ -39,6 +40,7 @@ app.use("/events", eventsRouter);
app.use("/notifications", notificationsRouter);
app.use("/registration", registrationRouter);
app.use("/s3", s3Router);
app.use("/stats", statsRouter);
app.use("/subscription", subscriptionRouter);

app.get("/status", (_, res) => {
Expand Down
41 changes: 39 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const Config = {
ALLOWED_CORS_ORIGIN_PATTERNS: [
new RegExp("(.*).reflectionsprojections.org(.*)"),
new RegExp("deploy-preview-[0-9]*(--rp2024.netlify.app)(.*)"),
new RegExp("(.*)localhost(.*)"),
new RegExp("(.*)127.0.0.1(.*)"),
],

ENV: Environment.parse(getEnv("ENV")),
Expand All @@ -25,10 +27,45 @@ export const Config = {
CLIENT_ID: getEnv("OAUTH_GOOGLE_CLIENT_ID"),
CLIENT_SECRET: getEnv("OAUTH_GOOGLE_CLIENT_SECRET"),

// AUTH_CALLBACK_URI_BASE: "http://localhost:3000/auth/callback/",
AUTH_CALLBACK_URI_BASE:
// "http://localhost:3000/auth/callback/",
"https://api.reflectionsprojections.org/auth/callback/",

// prettier-ignore
AUTH_ADMIN_WHITELIST: new Set([
// Dev Chairs/Code-Owners (reach out to these people for questions)
"[email protected]", // Aydan Pirani
"[email protected]", // Divya Koya

// Directors
"[email protected]", // Ojaswee Chaudhary
"[email protected]", // Ritika Vithani

// Committee Chairs
"[email protected]", // Adit Shah (Ops)
"[email protected]", // Arpit Bansal (Ops)
"[email protected]", // Aashna Mauskar (Marketing)
"[email protected]", // Cole Jordan (Marketing)
"[email protected]", // Divya Machineni (Corp)
"[email protected]", // Nancy Zhang (Design)
"[email protected]", // Preethi Gomathinayagam (Content)
"[email protected]", // Sailaja Nallacheruvu (Corp)
"[email protected]", // Sahana Hariharan (Design)
"[email protected]", // Yoshee Jain (Content)

// Dev Team
"[email protected]", // Aryan Bahl
"[email protected]", // Aryan Bhardwaj
"[email protected]", // Alex Yang
"[email protected]", // Dev Patel
"[email protected]", // Jacob Chang
"[email protected]", // Jeremy Wu
"[email protected]", // Manya Dua
"[email protected]", // Riya Patel
"[email protected]", // Ronit Anandani
"[email protected]", // Shreenija Daggavolu
]),

JWT_SIGNING_SECRET: getEnv("JWT_SIGNING_SECRET"),
JWT_EXPIRATION_TIME: "1 day",

Expand All @@ -47,5 +84,5 @@ export const Config = {
export const DeviceRedirects: Record<string, string> = {
web: "http://localhost:5173/",
dev: "https://api.reflectionsprojections.org/auth/dev/",
mobile: "https//www.reflectionsprojections.org/",
mobile: "https://www.reflectionsprojections.org/",
};
13 changes: 9 additions & 4 deletions src/database.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import mongoose, { Schema } from "mongoose";
import mongoose, { Schema, Document } from "mongoose";
import {
AttendeeSchema,
AttendeeValidator,
} from "./services/attendees/attendee-schema";
import { RoleValidator, RoleSchema } from "./services/auth/auth-schema";
import { EventSchema, EventValidator } from "./services/events/events-schema";
import {
EventSchema,
privateEventValidator,
} from "./services/events/events-schema";
import {
RegistrationSchema,
RegistrationValidator,
Expand Down Expand Up @@ -43,13 +46,15 @@ function initializeModel(
},
});

return mongoose.model(modelName, schema);
type objectType = Zod.infer<typeof object>;
interface modelType extends Document, objectType {}
return mongoose.model<modelType>(modelName, schema);
}

// Example usage
export const Database = {
ROLES: initializeModel("roles", RoleSchema, RoleValidator),
EVENTS: initializeModel("events", EventSchema, EventValidator),
EVENTS: initializeModel("events", EventSchema, privateEventValidator),
SUBSCRIPTIONS: initializeModel(
"subscriptions",
SubscriptionSchema,
Expand Down
28 changes: 27 additions & 1 deletion src/services/attendees/attendee-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,43 @@ const AttendeeValidator = z.object({
userId: z.string(),
name: z.string(),
email: z.string().email(),
events: z.array(z.string()),
dietaryRestrictions: z.string().array(),
allergies: z.string().array(),
hasCheckedIn: z.boolean().default(false),
points: z.number().min(0).default(0),
hasPriority: z.object({
dayOne: z.boolean(),
dayTwo: z.boolean(),
dayThree: z.boolean(),
dayFour: z.boolean(),
dayFive: z.boolean(),
}),
});

// Mongoose schema for attendee
const AttendeeSchema = new mongoose.Schema({
userId: { type: String, required: true, unique: true },
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
dietaryRestrictions: [{ type: String, required: true }],
events: [{ type: mongoose.Schema.Types.ObjectId, ref: "Event" }],
dietaryRestrictions: { type: [String], required: true },
allergies: { type: [String], required: true },
hasCheckedIn: { type: Boolean, default: false },
points: { type: Number, default: 0 },
hasPriority: {
type: new mongoose.Schema(
{
dayOne: { type: Boolean, default: false },
dayTwo: { type: Boolean, default: false },
dayThree: { type: Boolean, default: false },
dayFour: { type: Boolean, default: false },
dayFive: { type: Boolean, default: false },
},
{ _id: false }
),
default: () => ({}),
},
});

export { AttendeeSchema, AttendeeValidator };
17 changes: 8 additions & 9 deletions src/services/auth/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { Role } from "../auth/auth-models";

const authStrategies: Record<string, GoogleStrategy> = {};

for (const key in DeviceRedirects) {
authStrategies[key] = createGoogleStrategy(key);
}

const authRouter = Router();

// Add role to userId by email address (admin only endpoint)
Expand Down Expand Up @@ -52,16 +56,10 @@ authRouter.get("/login/:DEVICE/", (req, res) => {
return res.status(StatusCodes.BAD_REQUEST).send({ error: "BadDevice" });
}

// Check if we've already created an auth strategy for the device
// If not, create a new one
if (!(device in authStrategies)) {
authStrategies[device] = createGoogleStrategy(device);
}

// Use the pre-created strategy
passport.use(device, authStrategies[device]);
// passport.use(authStrategies[device]);

return passport.authenticate(device, {
return passport.authenticate(authStrategies[device], {
scope: ["profile", "email"],
})(req, res);
});
Expand All @@ -70,7 +68,7 @@ authRouter.get(
"/callback/:DEVICE",
(req, res, next) =>
// Check based on the pre-existing strategy name
passport.authenticate(req.params.DEVICE, {
passport.authenticate(authStrategies[req.params.DEVICE], {
session: false,
})(req, res, next),
async function (req, res, next) {
Expand All @@ -93,6 +91,7 @@ authRouter.get(
);
const redirectUri =
DeviceRedirects[req.params.DEVICE] + `?token=${token}`;
console.log(redirectUri);
return res.redirect(redirectUri);
} catch (error) {
next(error);
Expand Down
9 changes: 8 additions & 1 deletion src/services/auth/auth-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import { Config } from "../../config";
import { Database } from "../../database";
import { Role } from "./auth-models";

export function createGoogleStrategy(device: string) {
return new GoogleStrategy(
Expand All @@ -16,10 +17,16 @@ export function createGoogleStrategy(device: string) {
const userId = `user${profile.id}`;
const name = profile.displayName;
const email = profile._json.email;
const roles = [];

// Check if user is admin -> if so, add ADMIN role to their list
if (Config.AUTH_ADMIN_WHITELIST.has(email ?? "")) {
roles.push(Role.Enum.ADMIN);
}

Database.ROLES.findOneAndUpdate(
{ userId: userId },
{ userId, name, email },
{ userId, name, email, roles },
{ upsert: true }
)
.then(() => cb(null, profile))
Expand Down
21 changes: 14 additions & 7 deletions src/services/events/events-router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from "express";
import { StatusCodes } from "http-status-codes";
import { EventValidator } from "./events-schema";
import { publicEventValidator } from "./events-schema";
import { Database } from "../../database";

const eventsRouter = Router();
Expand Down Expand Up @@ -28,7 +28,7 @@ eventsRouter.get("/currentOrNext", async (req, res, next) => {

eventsRouter.post("/", async (req, res, next) => {
try {
const validatedData = EventValidator.parse(req.body);
const validatedData = publicEventValidator.parse(req.body);
const event = new Database.EVENTS(validatedData);
await event.save();
return res.sendStatus(StatusCodes.CREATED);
Expand All @@ -40,15 +40,19 @@ eventsRouter.post("/", async (req, res, next) => {
eventsRouter.get("/:EVENTID", async (req, res, next) => {
const eventId = req.params.EVENTID;
try {
const event = await Database.EVENTS.findOne({ eventId: eventId });
const unfiltered_event = await Database.EVENTS.findOne({
eventId: eventId,
});

if (!event) {
if (!unfiltered_event) {
return res
.status(StatusCodes.NOT_FOUND)
.json({ error: "DoesNotExist" });
}

return res.status(StatusCodes.OK).json(event.toObject());
const filtered_event = publicEventValidator.parse(unfiltered_event);

return res.status(StatusCodes.OK).json(filtered_event);
} catch (error) {
next(error);
}
Expand All @@ -57,8 +61,11 @@ eventsRouter.get("/:EVENTID", async (req, res, next) => {
// Get all events
eventsRouter.get("/", async (req, res, next) => {
try {
const events = await Database.EVENTS.find();
return res.status(StatusCodes.OK).json(events);
const unfiltered_events = await Database.EVENTS.find();
const filtered_events = unfiltered_events.map((unfiltered_event) => {
return publicEventValidator.parse(unfiltered_event.toJSON());
});
return res.status(StatusCodes.OK).json(filtered_events);
} catch (error) {
next(error);
}
Expand Down
12 changes: 10 additions & 2 deletions src/services/events/events-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { v4 as uuidv4 } from "uuid";

export const EventType = z.enum(["A", "B", "C"]);

export const EventValidator = z.object({
eventId: z.coerce.string().optional(),
export const publicEventValidator = z.object({
eventId: z.coerce.string(),
name: z.string(),
startTime: z.coerce.date(),
endTime: z.coerce.date(),
Expand All @@ -17,6 +17,10 @@ export const EventValidator = z.object({
eventType: EventType,
});

export const privateEventValidator = publicEventValidator.extend({
attendanceCount: z.number(),
});

export const EventSchema = new Schema({
eventId: {
type: String,
Expand Down Expand Up @@ -56,6 +60,10 @@ export const EventSchema = new Schema({
type: Boolean,
default: false,
},
attendanceCount: {
type: Number,
default: 0,
},
eventType: {
type: String,
required: true,
Expand Down
2 changes: 2 additions & 0 deletions src/services/registration/registration-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const RegistrationValidator = z.object({
graduation: z.string().nullable().optional(),
major: z.string().nullable().optional(),
dietaryRestrictions: z.string().array(),
allergies: z.string().array(),
age: z.number().nullable().optional(),
gender: z.string().nullable().optional(),
race: z.array(z.string()).nullable().optional(),
Expand All @@ -33,6 +34,7 @@ const RegistrationSchema = new mongoose.Schema({
graduation: { type: String, default: null },
major: { type: String, default: null },
dietaryRestrictions: [{ type: String, required: true }],
allergies: [{ type: String, required: true }],
age: { type: Number, default: null },
gender: { type: String, default: null },
race: [{ type: String }],
Expand Down
Loading

0 comments on commit 74c8f8e

Please sign in to comment.