Skip to content

Commit

Permalink
Merge branch 'main' into JC/seedscript-data
Browse files Browse the repository at this point in the history
  • Loading branch information
jcharlpretorius committed May 24, 2024
2 parents 2cfa28a + 3a207aa commit 543073c
Show file tree
Hide file tree
Showing 38 changed files with 2,561 additions and 2,086 deletions.
178 changes: 155 additions & 23 deletions backend/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
UserCreateType,
UserUpdateSchema,
UserUpdateType,
resetPasswordSchema,
resetPasswordChangePasswordSchema,
} from "../../shared/zodSchemas";
import prisma from "../prisma/client";
import { AppError, AppErrorName } from "../utils/AppError";
Expand Down Expand Up @@ -281,6 +283,17 @@ export const loginUser = async (

//checks if user is a student
if (existingUser.accountType === "Student") {
if (existingUser.firstTimeLogin) {
await prisma.user.update({
where: {
id: existingUser.id,
},
data: {
firstTimeLogin: false,
},
});
}

// find the amount of events the user has attended (participationStatus = Going, and endDate is in the past) inside the userEventResponse table
const attendedEvents = await prisma.userEventResponse.count({
where: {
Expand Down Expand Up @@ -333,10 +346,22 @@ export const loginUser = async (
attended: attendedEvents,
following: orgs,
type: "Student",
firstTimeLogin: existingUser.firstTimeLogin,
},
});
//checks if user is organization owner
} else if (existingUser.accountType === "ApprovedOrg") {
if (existingUser.firstTimeLogin) {
await prisma.user.update({
where: {
id: existingUser.id,
},
data: {
firstTimeLogin: false,
},
});
}

// Confirm password matches
const loginTokenPayload: loginJwtPayloadType = {
id: existingUser.id,
Expand Down Expand Up @@ -407,6 +432,7 @@ export const loginUser = async (
(UserOrganizationRole) => UserOrganizationRole.organization.events,
).length,
type: "Organization_Admin",
firstTimeLogin: existingUser.firstTimeLogin,
},
});
//user not a student, or an approved organization owner
Expand All @@ -430,29 +456,6 @@ export const logoutUser = async (req: Request, res: Response) => {
});
};

// will have to be changed later
// reset password
export const resetPassword = async (
req: Request,
res: Response,
next: NextFunction,
) => {
try {
const { email, password } = loginSchema.parse(req.body);

await prisma.user.update({
where: {
email: email,
},
data: {
password: password,
},
});
} catch (error: any) {
next(error);
}
};

// remove User
export const removeUserById = async (
req: Request,
Expand Down Expand Up @@ -1487,3 +1490,132 @@ export const testFirebaseToken = async (
next(error);
}
};

// The reset password flow goes as so:
// Reset Password function Send OTP
// 1. User enters their email and requests a password reset
// 2. A code is sent to the user's email that they can use to reset their password, it expires after 10 minutes

// Reset Password function, change password with OTP
// 3. User enters the code and a new password
// 4. The code is verified and the password is changed

export const resetPasswordSendOTP = async (
req: Request,
res: Response,
next: NextFunction,
) => {
try {
const { email } = resetPasswordSchema.parse(req.body);

const otp = generateOTP(6);
const otpExpiry = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes from now

const user = await prisma.user.update({
where: {
email,
},
data: {
otp,
otpExpiry,
},
});

if (!user) {
throw new AppError(
AppErrorName.NOT_FOUND_ERROR,
"User not found",
404,
true,
);
}

// Send the OTP to the user's email
const message = {
to: email,
subject: `Password Reset OTP: ${otp} - CampusBuddy`,
html: `Your OTP is: ${otp}`,
};

await transporter.sendMail(message);

res.status(200).json({
message: `OTP sent successfully - ${email}`,
});
} catch (error: any) {
next(error);
}
};

function generateOTP(length: number) {
const digits = "0123456789";
let otp = "";
for (let i = 0; i < length; i++) {
otp += digits[Math.floor(Math.random() * 10)];
}
return otp;
}

export const resetPasswordChangePassword = async (
req: Request,
res: Response,
next: NextFunction,
) => {
try {
const { email, otp, password } = resetPasswordChangePasswordSchema.parse(
req.body,
);

const user = await prisma.user.findUnique({
where: {
email,
},
});

if (!user) {
throw new AppError(
AppErrorName.NOT_FOUND_ERROR,
"User not found",
404,
true,
);
}

if (!user.otp || !user.otpExpiry) {
throw new AppError(
AppErrorName.INVALID_INPUT_ERROR,
"No OTP found for user",
400,
true,
);
}

if (user.otp !== otp || user.otpExpiry < new Date()) {
throw new AppError(
AppErrorName.INVALID_INPUT_ERROR,
"Invalid OTP",
400,
true,
);
}

const hashedPassword = await hashPassword(password);

await prisma.user.update({
where: {
email,
},
data: {
password: hashedPassword,
otp: null,
otpExpiry: null,
},
});

res.status(200).json({
message: "Password reset successfully",
});
} catch (error: any) {
next(error);
}
};
38 changes: 34 additions & 4 deletions backend/prisma/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ export const users: User[] = [
accountType: UserType.ApprovedOrg,
profilePic: "https://d2epenzoyf672m.cloudfront.net/pfp/doctorstrange.webp",
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: true,
},
{
id: ids.userIds[2],
Expand All @@ -203,28 +206,37 @@ export const users: User[] = [
accountType: UserType.PendingOrg,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: true,
},
{
id: ids.userIds[3],
firstName: "Tom",
lastName: "Dee",
email: "[email protected]",
firstName: "Aarsh",
lastName: "Shah",
email: "[email protected]",
password: "Hashed-password1238",
institutionId: ids.instituteIds[1],
accountType: UserType.Student,
profilePic: null,
degreeName: "Computer Science",
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[4],
firstName: "Aarsh",
firstName: "AS",
lastName: "Shah",
email: "[email protected]",
password: "password123",
institutionId: null,
accountType: UserType.Admin,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[5],
Expand All @@ -236,6 +248,9 @@ export const users: User[] = [
accountType: UserType.Student,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[6],
Expand All @@ -247,6 +262,9 @@ export const users: User[] = [
accountType: UserType.Student,
profilePic: null,
degreeName: "Arts",
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[7],
Expand All @@ -258,6 +276,9 @@ export const users: User[] = [
accountType: UserType.Student,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[8],
Expand All @@ -269,6 +290,9 @@ export const users: User[] = [
accountType: UserType.PendingOrg,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[9],
Expand All @@ -280,6 +304,9 @@ export const users: User[] = [
accountType: UserType.PendingOrg,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
{
id: ids.userIds[10],
Expand All @@ -291,6 +318,9 @@ export const users: User[] = [
accountType: UserType.ApprovedOrg,
profilePic: null,
degreeName: null,
otp: null,
otpExpiry: null,
firstTimeLogin: false,
},
];

Expand Down
5 changes: 5 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ model User {
degreeName String? @map("degree_name")
otp String?
otpExpiry DateTime?
firstTimeLogin Boolean @default(false) @map("signed_in")
@@index([email])
@@index([institutionId])
@@map("user")
Expand Down
6 changes: 4 additions & 2 deletions backend/routes/user.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
loginUser,
logoutUser,
removeUserById,
resetPassword,
updateUser,
verifyExistingOrgSignup,
verifyStudentSignup,
Expand All @@ -21,12 +20,16 @@ import {
removeProfilePic,
profilePageData,
testFirebaseToken,
resetPasswordSendOTP,
resetPasswordChangePassword,
} from "../controllers/user.controller";
import { verifyAuthentication } from "../middleware/verifyAuth";
import { upload } from "../utils/S3Uploader";

const router = express.Router();

router.post("/getOTP", resetPasswordSendOTP);
router.post("/changePassword", resetPasswordChangePassword);
router.get("/profile", verifyAuthentication, profilePageData);
router.get("/firebaseToken/:id", testFirebaseToken); // TODO: remove, for testing firebase token
router.get("/token", generateJWT); // TODO - Remove this endpoint - for testing only
Expand All @@ -40,7 +43,6 @@ router.get("/verify/organization/new/:token", verifyNewOrgSignup);
router.get("/verify/organization/:id/:token", verifyExistingOrgSignup);
router.post("/loginUser", loginUser);
router.post("/logoutUser", verifyAuthentication, logoutUser);
router.post("/resetPassword", resetPassword); // should we be authenticated?
router.get("/", verifyAuthentication, getAllUsers);
router.get("/me", verifyAuthentication, getLoggedInUser);
router.get("/:id", verifyAuthentication, getUserById);
Expand Down
Loading

0 comments on commit 543073c

Please sign in to comment.