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

feat: update user profile #440

Open
wants to merge 1 commit into
base: clean
Choose a base branch
from
Open
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
74 changes: 37 additions & 37 deletions src/controllers/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { BadRequest, ResourceNotFound } from "../middleware";
import { sendJsonResponse } from "../helpers";
import asyncHandler from "../middleware/asyncHandler";

const userService = new UserService();

class UserController {
/**
* @swagger
Expand Down Expand Up @@ -57,48 +59,46 @@ class UserController {
* description: Internal Server Error
*
*/
static getProfile = asyncHandler(
async (req: Request, res: Response, next: NextFunction) => {
const { id } = req.user;
static getProfile = asyncHandler(async (req: Request, res: Response, next: NextFunction) => {
const { id } = req.user;

if (!id) {
throw new BadRequest("Unauthorized! No ID provided");
}

if (!id) {
throw new BadRequest("Unauthorized! No ID provided");
}
if (!validate(id)) {
throw new BadRequest("Unauthorized! Invalid User Id Format");
}

if (!validate(id)) {
throw new BadRequest("Unauthorized! Invalid User Id Format");
}
const user = await UserService.getUserById(id);
if (!user) {
throw new ResourceNotFound("User Not Found!");
}

const user = await UserService.getUserById(id);
if (!user) {
throw new ResourceNotFound("User Not Found!");
}
if (user?.deletedAt || user?.is_deleted) {
throw new ResourceNotFound("User not found!");
}

if (user?.deletedAt || user?.is_deleted) {
throw new ResourceNotFound("User not found!");
}
sendJsonResponse(res, 200, "User profile details retrieved successfully", {
id: user.id,
first_name: user?.first_name,
last_name: user?.last_name,
profile_id: user?.profile?.id,
username: user?.profile?.username,
bio: user?.profile?.bio,
job_title: user?.profile?.jobTitle,
language: user?.profile?.language,
pronouns: user?.profile?.pronouns,
department: user?.profile?.department,
social_links: user?.profile?.social_links,
timezones: user?.profile?.timezones,
});
});

sendJsonResponse(
res,
200,
"User profile details retrieved successfully",
{
id: user.id,
first_name: user?.first_name,
last_name: user?.last_name,
profile_id: user?.profile?.id,
username: user?.profile?.username,
bio: user?.profile?.bio,
job_title: user?.profile?.jobTitle,
language: user?.profile?.language,
pronouns: user?.profile?.pronouns,
department: user?.profile?.department,
social_links: user?.profile?.social_links,
timezones: user?.profile?.timezones,
},
);
},
);
static updateUser = asyncHandler(async (req: Request, res: Response) => {
const user = await userService.updateUserProfile(req.params.id, req.body, req.file);
sendJsonResponse(res, 200, "Profile successfully updated", user);
});
}

export { UserController };
4 changes: 4 additions & 0 deletions src/routes/user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Router } from "express";
import { UserController } from "../controllers";
import { authMiddleware } from "../middleware";
import { multerConfig } from "../config/multer";

const upload = multerConfig.single("profile_pic_url");

const userRoute = Router();

userRoute.get("/users/me", authMiddleware, UserController.getProfile);
userRoute.put("/users/:id", authMiddleware, upload, UserController.updateUser);

export { userRoute };
74 changes: 72 additions & 2 deletions src/services/userservice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { User } from "../models/user";
import { Repository } from "typeorm";
import { User, Profile } from "../models";
import AppDataSource from "../data-source";
import { ResourceNotFound } from "../middleware";
import { ResourceNotFound, HttpError } from "../middleware";
import { cloudinary } from "../config/multer";
import { IUserProfileUpdate } from "../types";

export class UserService {
private userRepository: Repository<User>;
private profileRepository: Repository<Profile>;

constructor() {
this.userRepository = AppDataSource.getRepository(User);
this.profileRepository = AppDataSource.getRepository(Profile);
}

static async getUserById(id: string): Promise<User | null> {
const userRepository = AppDataSource.getRepository(User);
const user = await userRepository.findOne({
Expand All @@ -17,4 +28,63 @@ export class UserService {

return user;
}

public async updateUserProfile(id: string, payload: IUserProfileUpdate, file: Express.Multer.File): Promise<User> {
try {
const user = await this.userRepository.findOne({
where: { id },
relations: ["profile"],
});

if (!user) {
throw new ResourceNotFound("User not found");
}

const userUpdates: Partial<User> = {};
if (payload.first_name) userUpdates.first_name = payload.first_name;
if (payload.last_name) userUpdates.last_name = payload.last_name;
if (payload.phone) userUpdates.phone = payload.phone;

const profileUpdates: Partial<Profile> = {};
if (payload.username) profileUpdates.username = payload.username;
if (payload.jobTitle) profileUpdates.jobTitle = payload.jobTitle;
if (payload.pronouns) profileUpdates.pronouns = payload.pronouns;
if (payload.department) profileUpdates.department = payload.department;
if (payload.bio) profileUpdates.bio = payload.bio;
if (payload.social_links) profileUpdates.social_links = payload.social_links;
if (payload.language) profileUpdates.language = payload.language;
if (payload.region) profileUpdates.region = payload.region;
if (payload.timezones) profileUpdates.timezones = payload.timezones;

if (file) {
const oldImageUrl = user.profile.profile_pic_url;
if (oldImageUrl) {
const publicId = oldImageUrl.split("/").pop()?.split(".")[0];
await cloudinary.uploader.destroy(publicId);
}

const { secure_url } = await cloudinary.uploader.upload(file.path);
profileUpdates.profile_pic_url = secure_url;
}

await this.userRepository.update(user.id, userUpdates);
await this.profileRepository.update(user.profile.id, profileUpdates);

const updatedUser = await this.userRepository.findOne({
where: { id },
relations: ["profile"],
});

if (updatedUser) {
delete updatedUser.password;
}

return updatedUser as User;
} catch (error) {
if (error instanceof HttpError) {
throw error;
}
throw new HttpError(error.status || 500, error.message || error);
}
}
}
16 changes: 16 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,19 @@ export interface GoogleUser {
picture: string;
sub: string;
}

export interface IUserProfileUpdate {
first_name: string;
last_name: string;
phone: string;
username: string;
jobTitle: string;
pronouns: string;
department: string;
bio: string;
social_links: string[];
language: string;
region: string;
timezones: string;
profile_pic_url: string;
}
Loading