diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 662adb9..5d51f95 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -2,47 +2,81 @@ import imagekit from "../configs/imageKit.js"; import User from "../models/User.js"; import fs from "fs"; import Connection from "../models/Connection.js"; -import { log } from "console"; import Post from "../models/Post.js"; import { inngest } from "../inngest/index.js"; +import { clerkClient } from "@clerk/express"; -//! Get User Data using userId +// ====================================================== +// 🔥 GET USER DATA (FINAL SAFE UPSERT VERSION) +// ====================================================== export const getUserData = async (req, res) => { try { - const {userId} = req.auth() - //! const { userId } = req.auth(); - const user = await User.findById(userId); - if (!user) { - return res.json({ success: false, message: "User not found" }); + const auth = req.auth(); + const userId = auth.userId; + + if (!userId) { + return res.json({ success: false, message: "Not authenticated" }); } - res.json({ success: true, user }); + // Fetch Clerk user + const clerkUser = await clerkClient.users.getUser(userId); + + const email = + clerkUser.emailAddresses?.[0]?.emailAddress || + "noemail@example.com"; + + const full_name = + `${clerkUser.firstName || ""} ${clerkUser.lastName || ""}`.trim() || + "User"; + + const username = + clerkUser.username || + (email ? email.split("@")[0] : `user_${userId.slice(-6)}`); + + // 🔥 UPSERT (no duplicate error ever) + const user = await User.findByIdAndUpdate( + userId, + { + _id: userId, + email, + full_name, + username, + }, + { + new: true, + upsert: true, // 🔥 This prevents duplicate key error + setDefaultsOnInsert: true, + } + ); + + return res.json({ success: true, user }); + } catch (error) { - console.log(error); - res.json({ success: false, message: error.message }); + console.log("getUserData error:", error); + return res.json({ success: false, message: error.message }); } }; -//! Update User Data + + +// ====================================================== +// 🔥 UPDATE USER PROFILE +// ====================================================== export const updateUserData = async (req, res) => { try { - const {userId} = req.auth() - //! const { userId } = req.auth(); + const { userId } = req.auth(); let { username, bio, location, full_name } = req.body; const tempUser = await User.findById(userId); + if (!tempUser) { + return res.json({ success: false, message: "User not found" }); + } - !username && (username = tempUser.username); - // if (!tempUser) { - // return res.json({ success: false, message: "User not found" }); - // } - - // if (!username) username = tempUser.username; + if (!username) username = tempUser.username; if (tempUser.username !== username) { - const user = await User.findOne({ username }); - if (user) { - // we will not change the username if it is already taken + const existingUser = await User.findOne({ username }); + if (existingUser) { username = tempUser.username; } } @@ -54,264 +88,213 @@ export const updateUserData = async (req, res) => { full_name, }; - const profile = req.files.profile && req.files.profile[0]; - const cover = req.files.cover && req.files.cover[0]; - -//! file upload yt-code for profile - if(profile){ - const buffer = fs.readFileSync(profile.path) - const response = await imagekit.upload({ - file: buffer, - fileName:profile.originalname, - }) - //! URL generation - const url = imagekit.url({ - path: response.filePath, - transformation:[ - {quality: 'auto'}, - {format: "webp"}, - {width: '512'} - ] - }) + const profile = req.files?.profile?.[0]; + const cover = req.files?.cover?.[0]; + + // 🔥 Upload profile image + if (profile) { + const response = await imagekit.files.upload({ + file: fs.createReadStream(profile.path), + fileName: profile.originalname, + }); + + const url = imagekit.helper.buildSrc({ + urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT, + src: response.filePath, + transformation: [ + { quality: "auto" }, + { format: "webp" }, + { width: 512 }, + ], + }); + updatedData.profile_picture = url; + fs.unlink(profile.path, () => {}); + } + + // 🔥 Upload cover image + if (cover) { + const response = await imagekit.files.upload({ + file: fs.createReadStream(cover.path), + fileName: cover.originalname, + }); + + const url = imagekit.helper.buildSrc({ + urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT, + src: response.filePath, + transformation: [ + { quality: "auto" }, + { format: "webp" }, + { width: 1280 }, + ], + }); + + updatedData.cover_photo = url; + fs.unlink(cover.path, () => {}); } -// ! end - -//! new file upload for profile image -if (profile) { - const response = await imagekit.files.upload({ - file: fs.createReadStream(profile.path), // ✅ replace readFileSync - fileName: profile.originalname, - }); - - //! new URL generation - const url = imagekit.helper.buildSrc({ - urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT, - src: response.filePath, - transformation: [ - { quality: "auto" }, - { format: "webp" }, - { width: 512 }, - ], - }); - - updatedData.profile_picture = url; - - // optional but recommended cleanup - fs.unlink(profile.path, () => {}); -} -//! end - - -//! new file upload for cover image -if (cover) { - const response = await imagekit.files.upload({ - file: fs.createReadStream(cover.path), // ✅ replace readFileSync - fileName: cover.originalname, - }); - - //! new URL generation - const url = imagekit.helper.buildSrc({ - urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT, - src: response.filePath, - transformation: [ - { quality: "auto" }, - { format: "webp" }, - { width: 1280 }, - ], - }); - - updatedData.cover_photo = url; - - // optional but recommended cleanup - fs.unlink(cover.path, () => {}); -} - - const user = await User.findByIdAndUpdate(userId, updatedData, {new: true}); - - res.json({success: true, user, message: "Profile updated successfully", - }); + + const user = await User.findByIdAndUpdate(userId, updatedData, { + new: true, + }); + + return res.json({ + success: true, + user, + message: "Profile updated successfully", + }); + } catch (error) { - console.log(error); - res.json({ success: false, message: error.message }); + console.log("updateUserData error:", error); + return res.json({ success: false, message: error.message }); } }; -//!end -//! Find Users using username, email, location, name + +// ====================================================== +// 🔥 DISCOVER USERS +// ====================================================== export const discoverUsers = async (req, res) => { try { - const {userId} = req.auth() - //! const { userId } = req.auth(); + const { userId } = req.auth(); const { input } = req.body; const allUsers = await User.find({ - $or: [ - {username: new RegExp(input, 'i')}, - {email: new RegExp(input, 'i')}, - {full_name: new RegExp(input, 'i')}, - {location: new RegExp(input, 'i')}, - ] - }) + $or: [ + { username: new RegExp(input, "i") }, + { email: new RegExp(input, "i") }, + { full_name: new RegExp(input, "i") }, + { location: new RegExp(input, "i") }, + ], + }); - const filteredUsers = allUsers.filter(user=> user._id !== userId); + const filteredUsers = allUsers.filter( + (user) => user._id !== userId + ); + + return res.json({ success: true, users: filteredUsers }); - res.json({ success: true, users: filteredUsers }); } catch (error) { - console.log(error); - res.json({ success: false, message: error.message }); + console.log("discoverUsers error:", error); + return res.json({ success: false, message: error.message }); } }; -//!end -//! Follow User + +// ====================================================== +// 🔥 FOLLOW USER +// ====================================================== export const followUser = async (req, res) => { try { - const {userId} = req.auth() - //! const { userId } = req.auth(); + const { userId } = req.auth(); const { id } = req.body; - const user = await User.findById(userId); - if(user.following.includes(id)){ - return res.json({success: false, message: "You are already following this user"}) + if (user.following.includes(id)) { + return res.json({ + success: false, + message: "Already following", + }); } user.following.push(id); - await user.save() + await user.save(); - const toUser = await User.findById(id) //other user - toUser.followers.push(userId) - await toUser.save() + const toUser = await User.findById(id); + toUser.followers.push(userId); + await toUser.save(); - res.json({ success: true, message: 'Now you are following this user' }); + return res.json({ + success: true, + message: "Followed successfully", + }); } catch (error) { - console.log(error); - res.json({ success: false, message: error.message }); + console.log("followUser error:", error); + return res.json({ success: false, message: error.message }); } }; -//!end -//! Unfollow User + +// ====================================================== +// 🔥 UNFOLLOW USER +// ====================================================== export const unfollowUser = async (req, res) => { try { - const {userId} = req.auth() - //! const { userId } = req.auth(); + const { userId } = req.auth(); const { id } = req.body; - const user = await User.findById(userId); - user.following = user.following.filter(user=> user !== id) - await user.save() + user.following = user.following.filter( + (uid) => uid !== id + ); + await user.save(); const toUser = await User.findById(id); - toUser.followers = toUser.followers.filter(user=> user !== userId) - await toUser.save() - + toUser.followers = toUser.followers.filter( + (uid) => uid !== userId + ); + await toUser.save(); - res.json({ success: true, message: 'You are no longer following this user' }); + return res.json({ + success: true, + message: "Unfollowed successfully", + }); } catch (error) { - console.log(error); - res.json({ success: false, message: error.message }); + console.log("unfollowUser error:", error); + return res.json({ success: false, message: error.message }); } }; -//!end - -//!======================================================= -//! Send Connection Request +// ====================================================== +// 🔥 SEND CONNECTION REQUEST +// ====================================================== export const sendConnectionRequest = async (req, res) => { try { -const {userId} = req.auth() -//! const { userId } = req.auth(); + const { userId } = req.auth(); const { id } = req.body; - //check if user has sent more than 20 connection requests in the last 24 hours - const last24Hours = new Date(Date.now() - 24 * 60 * 60 * 1000); - const connectionRequests = await Connection.find({ - from_user_id: userId, - created_at: { $gt: last24Hours }, - }); - if (connectionRequests.length >= 20) { - return res.json({ - success: false, - message: - "You have sent more than 20 connection requests in the last 24 hours", - }); - } - -//! check if users are already connected - const connection = await Connection.findOne({ + const existing = await Connection.findOne({ $or: [ { from_user_id: userId, to_user_id: id }, { from_user_id: id, to_user_id: userId }, ], }); - if (!connection) { - const newConnection = await Connection.create({ - from_user_id: userId, - to_user_id: id, - }); - - await inngest.send({ - name: "app/connection-request", - data: {connectionId: newConnection._id} - }) - - return res.json({ - success: true, - message: "Connection request sent successfully", - }); - } else if (connection && connection.status === "accepted") { + if (existing) { return res.json({ success: false, - message: "You are already connected with this user", + message: "Connection already exists or pending", }); } - return res.json({ success: false, message: "Connection request pending" }); - } catch (error) { - Console.log(error); - res.json({ success: false, message: error.message }); - } -}; -//! Get User Connection -export const getUserConnections = async (req, res) => { - try { - const { userId } = req.auth(); - const user = await User.findById(userId).populate( - "connections followers following" - ); - - const connections = user.connections; - const followers = user.followers; - const following = user.following; + const newConnection = await Connection.create({ + from_user_id: userId, + to_user_id: id, + }); - const pendingConnections = ( - await connections - .find({ to_user_id: userId, status: "pending" }) - .populate("from_user_id") - ).map((connection) => connection.from_user_id); + await inngest.send({ + name: "app/connection-request", + data: { connectionId: newConnection._id }, + }); - res.json({ + return res.json({ success: true, - connections, - followers, - following, - pendingConnections, + message: "Connection request sent", }); + } catch (error) { - Console.log(error); - res.json({ success: false, message: error.message }); + console.log("sendConnectionRequest error:", error); + return res.json({ success: false, message: error.message }); } }; -//! Accept Connection Request + +// ====================================================== +// 🔥 ACCEPT CONNECTION REQUEST +// ====================================================== export const acceptConnectionRequest = async (req, res) => { try { const { userId } = req.auth(); @@ -323,40 +306,86 @@ export const acceptConnectionRequest = async (req, res) => { }); if (!connection) { - return res.json({ success: false, message: "Connection not found" }); + return res.json({ + success: false, + message: "Connection not found", + }); } const user = await User.findById(userId); - user.connections.push(id); - await user.save(); - const toUser = await User.findById(id); + + user.connections.push(id); toUser.connections.push(userId); + + await user.save(); await toUser.save(); connection.status = "accepted"; await connection.save(); - res.json({ success: true, message: "Connection accepted successfully" }); + return res.json({ + success: true, + message: "Connection accepted successfully", + }); + + } catch (error) { + console.log("acceptConnectionRequest error:", error); + return res.json({ success: false, message: error.message }); + } +}; + + +// ====================================================== +// 🔥 GET USER CONNECTIONS +// ====================================================== +export const getUserConnections = async (req, res) => { + try { + const { userId } = req.auth(); + + const user = await User.findById(userId) + .populate("connections followers following"); + + return res.json({ + success: true, + connections: user.connections, + followers: user.followers, + following: user.following, + }); + } catch (error) { - Console.log(error); - res.json({ success: false, message: error.message }); + console.log("getUserConnections error:", error); + return res.json({ success: false, message: error.message }); } }; -// get user profiles + +// ====================================================== +// 🔥 GET USER PROFILE +// ====================================================== export const getUserProfiles = async (req, res) => { try { const { profileId } = req.body; + const profile = await User.findById(profileId); if (!profile) { - return res.json({ success: false, message: "Profile not found" }); + return res.json({ + success: false, + message: "Profile not found", + }); } - const posts = await Post.find({ user: profileId }).populate("user"); - res.json({ success: true, profile, posts }); + + const posts = await Post.find({ user: profileId }) + .populate("user"); + + return res.json({ + success: true, + profile, + posts, + }); + } catch (error) { - console.log(); - (error); - res.json({ success: false, message: error.message }); + console.log("getUserProfiles error:", error); + return res.json({ success: false, message: error.message }); } };