Spaces:
Running
Running
| import User from "../models/user.model.js" | |
| import asyncHandler from "../utils/asyncHandler.js" | |
| import ApiError from "../utils/ApiError.js" | |
| import ApiResponse from "../utils/ApiResponse.js" | |
| import { expressRepre } from "@vashuthegreat/vexpress" | |
| import client from "../utils/RedisClient.js" | |
| import validator from "validator" | |
| import jwt from "jsonwebtoken" | |
| import { uploadOnCloudinary,deleteOnCloudinary } from "../utils/cloudinary.utils.js" | |
| import logger from "../logger/create.logger.js" | |
| const token_option={ httpOnly: true, secure: true }; | |
| const generateAccessRefreshToken = async (user: any) => { | |
| const refreshToken = await user.generateRefreshToken(); | |
| const accessToken = await user.generateAccessToken(); | |
| return {accessToken, refreshToken} | |
| } | |
| async function ValidateAnything(dict: Record<string, any>){ | |
| for(const [key,val] of Object.entries(dict)){ | |
| // Skip validation if value is null or undefined | |
| if(val === null || val === undefined) continue; | |
| if(key.toLowerCase()=='email'){ | |
| if(!validator.isEmail(String(val)) || (String(val)).trim()==''){ | |
| throw new ApiError(400,"Invalid email"); | |
| } | |
| } | |
| else if(key.toLowerCase()=='password'){ | |
| if(String(val).trim()=='' || String(val).length<6) { | |
| throw new ApiError(400,"password must be at least 6 characters long"); | |
| } | |
| } | |
| else if(key.toLowerCase()=='url'){ | |
| if(!validator.isURL(String(val)) || (String(val)).trim()==''){ | |
| throw new ApiError(400,"Invalid url"); | |
| } | |
| } | |
| } | |
| } | |
| export const createUser = expressRepre({ | |
| summary: "create user", | |
| body: { fullName: "Vansh Sharma", username:"vashuthegreat",email: "vanshsharma123@gmail.com", password: "122344544" }, | |
| response: "To create a user", | |
| }, asyncHandler(async(req,res)=>{ | |
| const {fullName, email, password, username} = req.body; | |
| logger.info(`Creating user with email: ${email}, username: ${username}`); | |
| if(!fullName || !email || !password || !username){ | |
| throw new ApiError(400,"All fields (fullName, email, password, username) are required"); | |
| } | |
| await ValidateAnything({email, password}); | |
| // Check if user already exists by email or username | |
| const existingUser = await User.findOne({ $or: [{email}, {username}] }); | |
| if (existingUser){ | |
| if (existingUser.email === email) { | |
| throw new ApiError(400,"email already taken, please choose another"); | |
| } | |
| throw new ApiError(400,"username already taken, please choose another"); | |
| } | |
| const user = new User({ | |
| fullName, | |
| email, | |
| password, | |
| username | |
| }); | |
| const {accessToken, refreshToken} = await generateAccessRefreshToken(user); | |
| user.refreshToken = refreshToken; | |
| await user.save(); | |
| const createdUser = await User.findById(user._id).select("-password -refreshToken"); | |
| if(!createdUser){ | |
| throw new ApiError(500,"Failed to register user"); | |
| } | |
| const userResponse = createdUser; | |
| logger.info(`User created successfully: ${userResponse?._id}`); | |
| res | |
| .cookie("refreshToken", refreshToken, token_option) | |
| .cookie("accessToken", accessToken, token_option) | |
| .json(new ApiResponse(200, userResponse, "User created successfully")); | |
| })) | |
| export const updateUser = expressRepre({ | |
| summary: "update user", | |
| body: { | |
| fullName: null, | |
| password: null, | |
| }, | |
| response: "user updated and the updated user " | |
| }, asyncHandler(async (req,res)=>{ | |
| const {fullName, password} = req.body; | |
| if(fullName && !fullName.trim()){ | |
| throw new ApiError(400,"Full name cannot be empty") | |
| } | |
| await ValidateAnything({password}); | |
| const user_id = req.user?._id; // Changed from .id to ._id | |
| if(!user_id){ | |
| throw new ApiError(401,"Unauthorized - User ID not found in token"); | |
| } | |
| const userInstance = await User.findById(user_id); | |
| if(!userInstance){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| if (fullName) userInstance.fullName = fullName; | |
| if (password) userInstance.password = password; | |
| await userInstance.save(); | |
| const userResponse = await User.findById(user_id).select("-password"); | |
| logger.info(`User updated: ${user_id}`); | |
| res.json(new ApiResponse(200, userResponse, "User updated successfully")); | |
| })) | |
| export const login = expressRepre({ | |
| summary: "login user by either email or email and password", | |
| body: { | |
| email: null, | |
| password: null, | |
| }, | |
| response: "user " | |
| }, asyncHandler(async (req,res)=>{ | |
| const {password, email: loginIdentifier} = req.body; | |
| logger.info(`Login attempt for: ${loginIdentifier}`); | |
| if(!password){ | |
| throw new ApiError(400,"Password is required"); | |
| } | |
| if(!loginIdentifier){ | |
| throw new ApiError(400,"Email or username is required"); | |
| } | |
| const trimmedIdentifier = loginIdentifier.trim(); | |
| const trimmedPassword = password.trim(); | |
| await ValidateAnything({password: trimmedPassword, email: trimmedIdentifier}); | |
| const userInstance = await User.findOne({ | |
| $or: [ | |
| { email: trimmedIdentifier }, | |
| { username: trimmedIdentifier.toLowerCase() } | |
| ] | |
| }); | |
| if(!userInstance){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| const isPasswordValid = await (userInstance as any).isPasswordCorrect(password); | |
| if(!isPasswordValid){ | |
| throw new ApiError(401,"Invalid credentials"); | |
| } | |
| const {accessToken, refreshToken} = await generateAccessRefreshToken(userInstance); | |
| userInstance.refreshToken = refreshToken; | |
| await userInstance.save(); | |
| const userResponse = await User.findById(userInstance._id).select("-password"); | |
| res | |
| .cookie("refreshToken", refreshToken, token_option) | |
| .cookie("accessToken", accessToken, token_option) | |
| .json(new ApiResponse(200, userResponse, "User logged in successfully")); | |
| })) | |
| export const Logout=expressRepre({ | |
| summary: "logout user ", | |
| response: "user logedout " | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const user = await User.findById(req.user?._id); | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| user.refreshToken = null; | |
| await user.save(); | |
| res.clearCookie("refreshToken", token_option) | |
| .clearCookie("accessToken", token_option) | |
| .json(new ApiResponse(200, "User logged out successfully")); | |
| logger.info(`User logged out: ${req.user?._id}`); | |
| }) | |
| ) | |
| export const uploadAvatar=expressRepre( | |
| { | |
| summary: "upload avatar", | |
| FormData:{ | |
| avatar: "avatar.png" | |
| }, | |
| response: "avatar uploaded" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const user=await User.findById(req.user?._id); | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| if (user.avatar){ | |
| await deleteOnCloudinary(user.avatar); | |
| } | |
| const avatar = (req.files as any)?.avatar?.[0]; | |
| console.log(avatar) | |
| if (!avatar){ | |
| throw new ApiError(400,"Avatar is required"); | |
| } | |
| const avatarUrl=await uploadOnCloudinary(avatar.path); | |
| if (!avatarUrl){ | |
| throw new ApiError(500,"Failed to upload avatar"); | |
| } | |
| user.avatar=avatarUrl.url; | |
| await user.save(); | |
| res.status(200).json(new ApiResponse(200, user, "Avatar uploaded successfully")); | |
| }) | |
| ) | |
| export const deleteAvatar=expressRepre( | |
| { | |
| summary: "delete avatar", | |
| response: "avatar deleted" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const user=await User.findById(req.user?._id); | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| if (!user.avatar){ | |
| throw new ApiError(400,"Avatar not found"); | |
| } | |
| await deleteOnCloudinary(user.avatar); | |
| // Directly update the database to set profileImage to NULL | |
| user.avatar = null; | |
| await user.save(); | |
| res.status(200).json(new ApiResponse(200, await User.findById(user._id).select("-password"), "Avatar deleted successfully")); | |
| }) | |
| ) | |
| export const addResume=expressRepre( | |
| { | |
| summary: "upload resume", | |
| query:{ | |
| resume_id: "696c7ba53a981f5c038d009b" | |
| }, | |
| response: "resume uploaded list of links" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const user=await User.findById(req.user?._id); | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| const resume=req.query?.resume_id | |
| console.log(resume) | |
| if (!resume){ | |
| throw new ApiError(400,"Resume is required"); | |
| } | |
| // const resumeUrl=await uploadOnCloudinary(resume.path); | |
| // if (!resumeUrl){ | |
| // throw new ApiError(500,"Failed to upload resume"); | |
| // } | |
| user.resumes.push(resume); | |
| await user.save(); | |
| res.status(200).json(new ApiResponse(200, user, "Resume uploaded successfully")); | |
| }) | |
| ) | |
| export const addAboutUser=expressRepre( | |
| { | |
| summary: "add about user list of strings", | |
| body:{ | |
| aboutUser: "user persuing btech user is ai engineer user is ai enthusiasm" | |
| }, | |
| response: "about user added list of strings" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const aboutUser=req.body.aboutUser | |
| console.log(aboutUser) | |
| if (!aboutUser){ | |
| throw new ApiError(400,"About user is required"); | |
| } | |
| const user=await User.findByIdAndUpdate(req.user?._id,{aboutUser},{new:true}); | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| res.status(200).json(new ApiResponse(200, user, "About user added successfully")); | |
| }) | |
| ) | |
| export const deleteResume=expressRepre( | |
| { | |
| summary: "delete resume", | |
| params:{ | |
| idx: 0 | |
| }, | |
| response: "resume deleted" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| const user=await User.findById(req.user?._id); | |
| const { idx } = req.params as { idx: string }; | |
| if(!user){ | |
| throw new ApiError(404,"User not found"); | |
| } | |
| if (user.resumes.length - 1 < Number(idx)) { | |
| throw new ApiError(400, "Resume not found"); | |
| } | |
| // Directly update the database to set profileImage to NULL | |
| user.resumes.splice(Number(idx), 1); | |
| await user.save(); | |
| res.status(200).json(new ApiResponse(200, await User.findById(user._id).select("-password"), "Resume deleted successfully")); | |
| }) | |
| ) | |
| export const getUserById = expressRepre( | |
| { | |
| summary: "send userId get userDetails", | |
| params: { id: '699a9bd773755482caa5996f' }, | |
| response: "userData", | |
| }, | |
| asyncHandler(async (req, res) => { | |
| const { id: _id } = req.params as { id: string }; | |
| console.log(req.params) | |
| if (!_id) { | |
| throw new ApiError(400, "User id is required"); | |
| } | |
| const user = await User.findById(_id).select("-password -refresstoken").lean(); | |
| if (!user) { | |
| throw new ApiError(404, "User does not exist"); | |
| } | |
| res.status(200).json(new ApiResponse(200, user, "User fetched successfully")); | |
| }) | |
| ); | |
| export const getUser=expressRepre( | |
| { | |
| summary:"simply hit get if logged in", | |
| response:"userData" | |
| }, | |
| asyncHandler( | |
| async(req,res)=>{ | |
| const _id=req.user?._id | |
| console.log(req.user._id) | |
| if(!_id){ | |
| throw new ApiError(400,"user id is required") | |
| } | |
| const user=await User.findById(_id).select("-password -refresstoken") | |
| if(!user){ | |
| throw new ApiError(300,"User does not exist") | |
| } | |
| res.status(200).json(new ApiResponse(200,user,"user fetched successfully")) | |
| } | |
| ) | |
| ) | |
| export const updateUserData=expressRepre( | |
| { | |
| summary:"Update user data json", | |
| body:{ | |
| "temp_data":`{ | |
| "personal": { | |
| "name": "John Doe", | |
| "title": "Software Engineer", | |
| "phone": "+1 234 567 890", | |
| "email": "john.doe@example.com", | |
| "location": "San Francisco, CA", | |
| "image": { | |
| "enabled": true, | |
| "url": "" | |
| } | |
| }, | |
| "summary": "Experienced software engineer with a strong background in full-stack development and a passion for building scalable applications.", | |
| "education": [ | |
| { | |
| "year": "2016 - 2020", | |
| "institute": "University of California, Berkeley", | |
| "degree": "Bachelor of Science in Computer Science" | |
| } | |
| ], | |
| "skills": [ | |
| "JavaScript", | |
| "Node.js", | |
| "React", | |
| "MongoDB", | |
| "Docker", | |
| "Leadership", | |
| "Communication", | |
| "Problem Solving" | |
| ], | |
| "soft_skills": [ | |
| "Leadership", | |
| "Communication", | |
| "Problem Solving" | |
| ], | |
| "languages": [ | |
| "English", | |
| "Spanish" | |
| ], | |
| "experience": [ | |
| { | |
| "role": "Senior Developer", | |
| "duration": "2021 - Present", | |
| "company": "Tech Solutions Inc.", | |
| "description": "Lead a team of 5 developers in building a high-traffic e-commerce platform. Implemented microservices architecture using Node.js and AWS. Optimized database queries, reducing response time by 30%." | |
| }, | |
| { | |
| "role": "Junior Developer", | |
| "duration": "2020 - 2021", | |
| "company": "Startup Hub", | |
| "description": "Developed and maintained RESTful APIs for a mobile application. Collaborated with UI/UX designers to implement responsive web interfaces. Participated in code reviews and unit testing." | |
| } | |
| ], | |
| "projects": [ | |
| { | |
| "title": "AI Resume Builder", | |
| "description": "A web application that generates multiple resume templates dynamically using AI and user data.", | |
| "tech_stack": ["React", "Node.js", "EJS"] | |
| } | |
| ], | |
| "certifications": [ | |
| "AWS Certified Developer", | |
| "Full Stack Web Development" | |
| ], | |
| "achievements": [ | |
| "Employee of the Month", | |
| "Winner of Hackathon 2023" | |
| ], | |
| "references": [ | |
| { | |
| "name": "Jane Smith", | |
| "company": "Tech Solutions Inc.", | |
| "position": "CTO", | |
| "phone": "+1 987 654 321", | |
| "email": "jane.smith@techsolutions.com" | |
| } | |
| ], | |
| "someImportantUrls": { | |
| "GitHub": "https://github.com/johndoe", | |
| "LinkedIn": "https://linkedin.com/in/johndoe", | |
| "Portfolio": "https://johndoe.com" | |
| } | |
| }` | |
| }, | |
| response:"updated user" | |
| }, | |
| asyncHandler(async (req,res)=>{ | |
| let {temp_data}=req.body; | |
| const user_id=req.user?._id; | |
| if (!temp_data){ | |
| throw new ApiError(400, "temp_data is required"); | |
| } | |
| if (typeof temp_data === 'string') { | |
| try { | |
| temp_data = JSON.parse(temp_data); | |
| } catch (error) { | |
| // If it fails to parse, it will just stay as a string, which is fine | |
| } | |
| } | |
| console.log(temp_data); | |
| const user=await User.findByIdAndUpdate(user_id,{temp_data:temp_data},{new:true}).select("-password -refreshToken"); | |
| if (!user){ | |
| throw new ApiError(404, "User not found"); | |
| } | |
| return res.status(200).json(new ApiResponse(200,user)); | |
| }) | |
| ) | |
| export const refreshAccessToken=expressRepre({ | |
| summary:"Use this get route for refreshing tokens", | |
| response:"Token refreshed" | |
| },asyncHandler(async (req,res)=>{ | |
| const incommingRefreshToken=req.cookies.refreshToken||req.body.refreshToken; | |
| if(!incommingRefreshToken) throw new ApiError(401,"unautherized request"); | |
| const decoded_token = jwt.verify(incommingRefreshToken, process.env.REFRESH_TOKEN_SECRET as string) as jwt.JwtPayload; | |
| const user = await User.findById(decoded_token?._id); | |
| if (incommingRefreshToken !== user?.refreshToken) { | |
| throw new ApiError(401, "Refresh token is expired or used"); | |
| } | |
| const { accessToken, refreshToken: newRefreshToken } = await generateAccessRefreshToken(user); | |
| if (user) { | |
| user.refreshToken = newRefreshToken; | |
| await user.save({ validateBeforeSave: false }); | |
| } | |
| return res.status(200) | |
| .cookie("accessToken",accessToken,token_option) | |
| .cookie("refreshToken",newRefreshToken,token_option) | |
| .json( | |
| new ApiResponse( | |
| 200, | |
| {accessToken,refreshToken:newRefreshToken}, | |
| "Access token refreshed" | |
| ) | |
| ) | |
| })) | |