Skip to content

Commit

Permalink
Merge pull request #10 from nicolelim02/feat/users
Browse files Browse the repository at this point in the history
Add user profile page
  • Loading branch information
ruiqi7 authored Sep 24, 2024
2 parents 6ea317a + 4e94624 commit e4fbc17
Show file tree
Hide file tree
Showing 17 changed files with 721 additions and 67 deletions.
3 changes: 3 additions & 0 deletions backend/user-service/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ JWT_SECRET=you-can-replace-this-with-your-own-secret
ADMIN_USERNAME=administrator
ADMIN_EMAIL=[email protected]
ADMIN_PASSWORD=Admin@123

# origins for cors
ORIGINS=["http://localhost:5173", "http://127.0.0.1:5173"]
35 changes: 29 additions & 6 deletions backend/user-service/app.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
import express, { Request, Response, NextFunction } from "express";
import cors from "cors";
import dotenv from "dotenv";
import fs from "fs";
import yaml from "yaml";
import swaggerUi from "swagger-ui-express";

import userRoutes from "./routes/user-routes.js";
import authRoutes from "./routes/auth-routes.js";

dotenv.config();

const file = fs.readFileSync("./swagger.yml", "utf-8");
const swaggerDocument = yaml.parse(file);
const origin = process.env.ORIGINS
? process.env.ORIGINS.split(",")
: ["http://localhost:5173", "http://127.0.0.1:5173"];

const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cors()); // config cors so that front-end can use
app.options("*", cors());
app.use(
cors({
origin: origin,
credentials: true,
})
); // config cors so that front-end can use
app.options(
"*",
cors({
origin: ["http://localhost:5173", "http://127.0.0.1:5173"],
credentials: true,
})
);

// To handle CORS Errors
app.use((req: Request, res: Response, next: NextFunction) => {
res.header("Access-Control-Allow-Origin", "*"); // "*" -> Allow all links to access
res.header("Access-Control-Allow-Origin", req.headers.origin); // "*" -> Allow all links to access

res.header(
"Access-Control-Allow-Headers",
Expand All @@ -30,9 +53,9 @@ app.use((req: Request, res: Response, next: NextFunction) => {
next();
});

app.use("/users", userRoutes);
app.use("/auth", authRoutes);

app.use("/api/users", userRoutes);
app.use("/api/auth", authRoutes);
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.get("/", (req: Request, res: Response, next: NextFunction) => {
console.log("Sending Greetings!");
res.json({
Expand Down
16 changes: 11 additions & 5 deletions backend/user-service/controller/auth-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { findUserByEmail as _findUserByEmail } from "../model/repository.js";
import { formatUserResponse } from "./user-controller.js";
import { AuthenticatedRequest } from "../types/request.js";

export async function handleLogin(req: AuthenticatedRequest, res: Response): Promise<Response> {
export async function handleLogin(
req: AuthenticatedRequest,
res: Response
): Promise<Response> {
const { email, password } = req.body;
if (email && password) {
try {
Expand All @@ -28,9 +31,10 @@ export async function handleLogin(req: AuthenticatedRequest, res: Response): Pro
expiresIn: "7d",
}
);
return res
.status(200)
.json({ message: "User logged in", data: { accessToken, ...formatUserResponse(user) } });
return res.status(200).json({
message: "User logged in",
data: { accessToken, user: formatUserResponse(user) },
});
} catch (err) {
return res.status(500).json({ message: "Server error", err });
}
Expand All @@ -45,7 +49,9 @@ export async function handleVerifyToken(
): Promise<Response> {
try {
const verifiedUser = req.user;
return res.status(200).json({ message: "Token verified", data: verifiedUser });
return res
.status(200)
.json({ message: "Token verified", data: verifiedUser });
} catch (err) {
return res.status(500).json({ message: "Server error", err });
}
Expand Down
125 changes: 91 additions & 34 deletions backend/user-service/controller/user-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,34 @@ import {
} from "../utils/validators";
import { IUser } from "../model/user-model";

export async function createUser(req: Request, res: Response): Promise<Response> {
export async function createUser(
req: Request,
res: Response
): Promise<Response> {
try {
const { username, email, password } = req.body;
const existingUser = await _findUserByUsernameOrEmail(username, email);
if (existingUser) {
return res.status(409).json({ message: "username or email already exists" });
return res
.status(409)
.json({ message: "username or email already exists" });
}

if (username && email && password) {
const { isValid: isValidUsername, message: usernameMessage } = validateUsername(username);
const { isValid: isValidUsername, message: usernameMessage } =
validateUsername(username);
if (!isValidUsername) {
return res.status(400).json({ message: usernameMessage });
}

const { isValid: isValidEmail, message: emailMessage } = validateEmail(email);
const { isValid: isValidEmail, message: emailMessage } =
validateEmail(email);
if (!isValidEmail) {
return res.status(400).json({ message: emailMessage });
}

const { isValid: isValidPassword, message: passwordMessage } = validatePassword(password);
const { isValid: isValidPassword, message: passwordMessage } =
validatePassword(password);
if (!isValidPassword) {
return res.status(400).json({ message: passwordMessage });
}
Expand All @@ -53,11 +61,15 @@ export async function createUser(req: Request, res: Response): Promise<Response>
data: formatUserResponse(createdUser),
});
} else {
return res.status(400).json({ message: "username and/or email and/or password are missing" });
return res
.status(400)
.json({ message: "username and/or email and/or password are missing" });
}
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when creating new user!" });
return res
.status(500)
.json({ message: "Unknown error when creating new user!" });
}
}

Expand All @@ -72,30 +84,59 @@ export async function getUser(req: Request, res: Response): Promise<Response> {
if (!user) {
return res.status(404).json({ message: `User ${userId} not found` });
} else {
return res.status(200).json({ message: `Found user`, data: formatUserResponse(user) });
return res
.status(200)
.json({ message: `Found user`, data: formatUserResponse(user) });
}
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when getting user!" });
return res
.status(500)
.json({ message: "Unknown error when getting user!" });
}
}

export async function getAllUsers(req: Request, res: Response): Promise<Response> {
export async function getAllUsers(
req: Request,
res: Response
): Promise<Response> {
try {
const users = await _findAllUsers();

return res.status(200).json({ message: `Found users`, data: users.map(formatUserResponse) });
return res
.status(200)
.json({ message: `Found users`, data: users.map(formatUserResponse) });
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when getting all users!" });
return res
.status(500)
.json({ message: "Unknown error when getting all users!" });
}
}

export async function updateUser(req: Request, res: Response): Promise<Response> {
export async function updateUser(
req: Request,
res: Response
): Promise<Response> {
try {
const { username, email, password, profilePictureUrl, firstName, lastName, biography } =
req.body;
if (username || email || password || profilePictureUrl || firstName || lastName || biography) {
const {
username,
email,
password,
profilePictureUrl,
firstName,
lastName,
biography,
} = req.body;
if (
username ||
email ||
password ||
profilePictureUrl ||
firstName ||
lastName ||
biography
) {
const userId = req.params.id;

if (!isValidObjectId(userId)) {
Expand All @@ -108,7 +149,8 @@ export async function updateUser(req: Request, res: Response): Promise<Response>
}

if (username) {
const { isValid: isValidUsername, message: usernameMessage } = validateUsername(username);
const { isValid: isValidUsername, message: usernameMessage } =
validateUsername(username);
if (!isValidUsername) {
return res.status(400).json({ message: usernameMessage });
}
Expand All @@ -120,7 +162,8 @@ export async function updateUser(req: Request, res: Response): Promise<Response>
}

if (email) {
const { isValid: isValidEmail, message: emailMessage } = validateEmail(email);
const { isValid: isValidEmail, message: emailMessage } =
validateEmail(email);
if (!isValidEmail) {
return res.status(400).json({ message: emailMessage });
}
Expand All @@ -133,7 +176,8 @@ export async function updateUser(req: Request, res: Response): Promise<Response>

let hashedPassword: string | undefined;
if (password) {
const { isValid: isValidPassword, message: passwordMessage } = validatePassword(password);
const { isValid: isValidPassword, message: passwordMessage } =
validatePassword(password);
if (!isValidPassword) {
return res.status(400).json({ message: passwordMessage });
}
Expand All @@ -143,20 +187,16 @@ export async function updateUser(req: Request, res: Response): Promise<Response>
}

if (firstName) {
const { isValid: isValidFirstName, message: firstNameMessage } = validateName(
firstName,
"first name"
);
const { isValid: isValidFirstName, message: firstNameMessage } =
validateName(firstName, "first name");
if (!isValidFirstName) {
return res.status(400).json({ message: firstNameMessage });
}
}

if (lastName) {
const { isValid: isValidLastName, message: lastNameMessage } = validateName(
lastName,
"last name"
);
const { isValid: isValidLastName, message: lastNameMessage } =
validateName(lastName, "last name");
if (!isValidLastName) {
return res.status(400).json({ message: lastNameMessage });
}
Expand Down Expand Up @@ -192,11 +232,16 @@ export async function updateUser(req: Request, res: Response): Promise<Response>
}
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when updating user!" });
return res
.status(500)
.json({ message: "Unknown error when updating user!" });
}
}

export async function updateUserPrivilege(req: Request, res: Response): Promise<Response> {
export async function updateUserPrivilege(
req: Request,
res: Response
): Promise<Response> {
try {
const { isAdmin } = req.body;

Expand All @@ -211,7 +256,10 @@ export async function updateUserPrivilege(req: Request, res: Response): Promise<
return res.status(404).json({ message: `User ${userId} not found` });
}

const updatedUser = await _updateUserPrivilegeById(userId, isAdmin === true);
const updatedUser = await _updateUserPrivilegeById(
userId,
isAdmin === true
);
return res.status(200).json({
message: `Updated privilege for user ${userId}`,
data: formatUserResponse(updatedUser as IUser),
Expand All @@ -221,11 +269,16 @@ export async function updateUserPrivilege(req: Request, res: Response): Promise<
}
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when updating user privilege!" });
return res
.status(500)
.json({ message: "Unknown error when updating user privilege!" });
}
}

export async function deleteUser(req: Request, res: Response): Promise<Response> {
export async function deleteUser(
req: Request,
res: Response
): Promise<Response> {
try {
const userId = req.params.id;
if (!isValidObjectId(userId)) {
Expand All @@ -237,10 +290,14 @@ export async function deleteUser(req: Request, res: Response): Promise<Response>
}

await _deleteUserById(userId);
return res.status(200).json({ message: `Deleted user ${userId} successfully` });
return res
.status(200)
.json({ message: `Deleted user ${userId} successfully` });
} catch (err) {
console.error(err);
return res.status(500).json({ message: "Unknown error when deleting user!" });
return res
.status(500)
.json({ message: "Unknown error when deleting user!" });
}
}

Expand Down
4 changes: 3 additions & 1 deletion backend/user-service/model/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export async function findUserById(userId: string): Promise<IUser | null> {
return UserModel.findById(userId);
}

export async function findUserByUsername(username: string): Promise<IUser | null> {
export async function findUserByUsername(
username: string
): Promise<IUser | null> {
return UserModel.findOne({ username });
}

Expand Down
Loading

0 comments on commit e4fbc17

Please sign in to comment.