Skip to content

Commit

Permalink
Merge branch 'main' into dev/sdagg9/rate-limiter
Browse files Browse the repository at this point in the history
  • Loading branch information
sdagg9 authored Apr 24, 2024
2 parents 57e2bdf + 61af94e commit 3f6f368
Show file tree
Hide file tree
Showing 30 changed files with 932 additions and 78 deletions.
15 changes: 5 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
name: Prettier on Push and PR
name: Build

on:
push:
branches:
- "main"
- "dev**"
pull_request:
branches:
- "main"

jobs:
prettier:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "18.x"
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Run Prettier
run: npx prettier --write .
- name: Build
run: yarn build
11 changes: 3 additions & 8 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
name: Lint on Push and PR
name: Lint

on:
push:
branches:
- "main"
- "dev**"
pull_request:
branches:
- "main"

jobs:
lint:
Expand All @@ -18,7 +12,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: "18.x"
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Run ESLint
run: npx eslint .
run: npx eslint .
19 changes: 19 additions & 0 deletions .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Prettier

on:
push:

jobs:
prettier:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "18.x"
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Run Prettier
run: npx prettier --write .
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# rp-api
# API for Reflections | Projections 2024

Contributors:

- Aydan Pirani
- Divya Koya
- Riya Patel
- Jacob Chang
- Alex Yang
- Shreenija Daggavolu
22 changes: 22 additions & 0 deletions appspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/rp-api
file_exists_behavior: OVERWRITE
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
AfterInstall:
- location: scripts/build.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
- location: scripts/reload_server.sh
timeout: 300
runas: root
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "ENV=DEVELOPMENT nodemon -w src/ -x tsx src/app.ts",
"start": "ENV=PRODUCTION tsx src/app.ts",
"dev": "nodemon -w src/ -x tsx src/app.ts",
"start": "tsx src/app.ts",
"lint": "yarn prettier . --write",
"kill": "kill -9 $(lsof -t -i :3000)",
"build": "tsc"
Expand All @@ -13,8 +13,10 @@
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
"@types/express-rate-limit": "^6.0.0",
"@types/jsonwebtoken": "^9.0.6",
"@types/morgan": "^1.9.9",
"@types/node": "^20.9.3",
"@types/passport-google-oauth20": "^2.0.14",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"cross-env": "^7.0.3",
"eslint": "8.2.0",
Expand All @@ -34,13 +36,18 @@
"dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"axios": "^1.6.8",
"@types/cors": "^2.8.17",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.1",
"express-rate-limit": "^6.0.0",
"http-status-codes": "^2.3.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.3",
"morgan": "^1.10.0",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"tsx": "^4.5.0",
"typescript": "*",
"zod": "^3.22.4"
Expand Down
3 changes: 3 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd /home/ubuntu/rp-api
yarn build
3 changes: 3 additions & 0 deletions scripts/install_dependencies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd /home/ubuntu/rp-api
sudo yarn
3 changes: 3 additions & 0 deletions scripts/reload_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd /home/ubuntu/rp-api
sudo pm2 reload RP_API
18 changes: 15 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import express from "express";
import { StatusCodes } from "http-status-codes";
import morgan from "morgan";
import bodyParser from "body-parser";
import { Config } from "./config";
import { connectToDatabase } from "./utilities";
import { rateLimiter } from "./middleware/rateLimiter";

import cors from "cors";
import morgan from "morgan";
import bodyParser from "body-parser";
import errorHandler from "./middleware/error-handler";

import attendeeRouter from "./services/attendees/attendee-router";
import authRouter from "./services/auth/auth-router";
import eventRouter from "./services/events/event-router";
import registrationRouter from "./services/registration/registration-router";
import subscriptionRouter from "./services/subscription/subscription-router";

const app = express();

Expand All @@ -18,15 +23,21 @@ app.disable("etag");

app.use(rateLimiter);

app.use(cors());

// To display the logs every time
app.use("/", morgan("dev"));

app.use("/", bodyParser.json());

// API routes
app.use("/attendee", attendeeRouter);
app.use("/auth", authRouter);
app.use("/event", eventRouter);
app.use("/registration", registrationRouter);
app.use("/subscription", subscriptionRouter);

app.get("/status", (_, res) => {
console.log(StatusCodes.OK);
return res.status(StatusCodes.OK).send("API is alive!");
});

Expand All @@ -38,5 +49,6 @@ app.use(errorHandler);

app.listen(Config.DEFAULT_APP_PORT, async () => {
await connectToDatabase();
process.send?.("ready");
console.log("Server is listening on port 3000...");
});
17 changes: 17 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,28 @@ dotenv.config();

export const Environment = z.enum(["PRODUCTION", "DEVELOPMENT", "TESTING"]);

export const MailingListName = z.enum(["rp_interest"]);

export const Config = {
DEFAULT_APP_PORT: 3000,
ENV: Environment.parse(getEnv("ENV")),

DATABASE_USERNAME: getEnv("DATABASE_USERNAME"),
DATABASE_PASSWORD: getEnv("DATABASE_PASSWORD"),
DATABASE_HOST: getEnv("DATABASE_HOST"),

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:
"https://api.reflectionsprojections.org/auth/callback/",

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

export const DeviceRedirects: Record<string, string> = {
web: "https://www.google.com/",
dev: "https://api.reflectionsprojections.org/auth/dev/",
};
29 changes: 27 additions & 2 deletions src/database.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import mongoose, { Schema } from "mongoose";
import { RoleInfo, RoleSchema } from "./services/auth/auth-schema";
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 {
RegistrationSchema,
RegistrationValidator,
} from "./services/registration/registration-schema";
import {
SubscriptionSchemaValidator,
SubscriptionSchema,
} from "./services/subscription/subscription-schema";

mongoose.set("toObject", { versionKey: false });

Expand Down Expand Up @@ -31,5 +44,17 @@ function initializeModel(

// Example usage
export const Database = {
ROLES: initializeModel("roles", RoleSchema, RoleInfo),
ROLES: initializeModel("roles", RoleSchema, RoleValidator),
EVENTS: initializeModel("events", EventSchema, EventValidator),
SUBSCRIPTIONS: initializeModel(
"subscriptions",
SubscriptionSchema,
SubscriptionSchemaValidator
),
ATTENDEES: initializeModel("attendees", AttendeeSchema, AttendeeValidator),
REGISTRATION: initializeModel(
"registration",
RegistrationSchema,
RegistrationValidator
),
};
25 changes: 25 additions & 0 deletions src/middleware/cors-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// import cors from "cors";

// // Allow CORS for Netlify deploy previews
// const allowedOrigins = ["https://reflectionsprojections.org"];
// // Function to check if the origin matches the deploy preview format
// function isNetlifyDeployPreview(origin: string) {
// const regex = new RegExp("deploy-preview-[0-9]*(--rp2024.netlify.app)(.*)");
// return regex.test(origin);
// }
//
// const corsMiddleware = cors({
// origin: function (origin, callback) {
// if (
// !origin ||
// allowedOrigins.includes(origin) ||
// isNetlifyDeployPreview(origin)
// ) {
// callback(null, true);
// } else {
// callback(new Error("Not allowed by CORS"));
// }
// },
// });

// export default cors();
67 changes: 67 additions & 0 deletions src/middleware/role-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { NextFunction, Request, Response } from "express";
import { JwtPayloadValidator, Role } from "../services/auth/auth-models";
import { z } from "zod";
import jsonwebtoken from "jsonwebtoken";
import { Config } from "../config";
import { StatusCodes } from "http-status-codes";

export default function RoleChecker(
requiredRoles: z.infer<typeof Role>[],
weakVerification: boolean = false
) {
return function (req: Request, res: Response, next: NextFunction) {
const jwt = req.headers.authorization;

if (jwt == undefined) {
if (weakVerification) {
next();
}

return res.status(StatusCodes.BAD_REQUEST).json({ error: "NoJWT" });
}

try {
const payloadData = jsonwebtoken.verify(
jwt,
Config.JWT_SIGNING_SECRET
);

const payload = JwtPayloadValidator.parse(payloadData);
res.locals.payload = payload;

const error = new Error("InvalidRoles");
const userRoles = payload.roles;

if (weakVerification) {
next();
}

if (requiredRoles.length == 0) {
next();
}

// Admins (staff) can access any endpoint
if (userRoles.includes(Role.Enum.ADMIN)) {
next();
}

// Corporate role can access corporate only endpoints
if (requiredRoles.includes(Role.Enum.CORPORATE)) {
if (userRoles.includes(Role.Enum.CORPORATE)) {
next();
}
}

// Need to be a user to access user endpoints (app users)
if (requiredRoles.includes(Role.Enum.USER)) {
if (userRoles.includes(Role.Enum.USER)) {
next();
}
}

throw error;
} catch (error) {
next(error);
}
};
}
Loading

0 comments on commit 3f6f368

Please sign in to comment.