Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user profile page #10

Merged
merged 8 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
[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