import express from "express"; import axios from "axios"; import config from "config"; import auth from "../../middleware/auth"; import { check, validationResult } from "express-validator"; // bring in normalize to give us a proper url, regardless of what user entered import normalize from "normalize-url"; import checkObjectId from "../../middleware/checkObjectId"; import Profile from "../../models/Profile"; import User from "../../models/User"; import Post from "../../models/Post"; import { isUserId } from "../../utils"; const router = express.Router(); // @route GET api/profile/me // @desc Get current users profile // @access Private router.get("/me", auth, async (req, res) => { try { if (isUserId(req)) { const profile = await Profile.findOne({ user: req.user.id, }).populate("user", ["name", "avatar"]); if (!profile) { return res .status(400) .json({ msg: "There is no profile for this user" }); } res.json(profile); } } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); res.status(500).send("Server Error"); } }); // @route POST api/profile // @desc Create or update user profile // @access Private router.post( "/", auth, check("status", "Status is required").notEmpty(), check("skills", "Skills is required").notEmpty(), async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // destructure the request const { website, skills, youtube, twitter, instagram, linkedin, facebook, // spread the rest of the fields we don't need to check ...rest } = req.body; // build a profile if (isUserId(req)) { const profileFields = { user: req.user.id, website: website && website !== "" ? normalize(website, { forceHttps: true }) : "", skills: Array.isArray(skills) ? skills : skills.split(",").map((skill: string) => " " + skill.trim()), ...rest, }; // Build socialFields object const socialFields: { [key: string]: any } = { youtube, twitter, instagram, linkedin, facebook, }; // normalize social fields to ensure valid url for (const [key, value] of Object.entries(socialFields)) { if (value && value.length > 0) socialFields[key] = normalize(value, { forceHttps: true }); } // add to profileFields profileFields.social = socialFields; try { // Using upsert option (creates new doc if no match is found): let profile = await Profile.findOneAndUpdate( { user: req.user.id }, { $set: profileFields }, { new: true, upsert: true, setDefaultsOnInsert: true }, ); return res.json(profile); } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); return res.status(500).send("Server Error"); } } }, ); // @route GET api/profile // @desc Get all profiles // @access Public router.get("/", async (req, res) => { try { const profiles = await Profile.find().populate("user", ["name", "avatar"]); res.json(profiles); } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); res.status(500).send("Server Error"); } }); // @route GET api/profile/user/:user_id // @desc Get profile by user ID // @access Public router.get( "/user/:user_id", checkObjectId("user_id"), async ({ params: { user_id } }, res) => { try { const profile = await Profile.findOne({ user: user_id, }).populate("user", ["name", "avatar"]); if (!profile) return res.status(400).json({ msg: "Profile not found" }); return res.json(profile); } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); return res.status(500).json({ msg: "Server error" }); } }, ); // @route DELETE api/profile // @desc Delete profile, user & posts // @access Private router.delete("/", auth, async (req, res) => { try { // Remove user posts // Remove profile // Remove user if (isUserId(req)) await Promise.all([ Post.deleteMany({ user: req.user.id }), Profile.findOneAndRemove({ user: req.user.id }), User.findOneAndRemove({ _id: req.user.id }), ]); res.json({ msg: "User deleted" }); } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); res.status(500).send("Server Error"); } }); // @route PUT api/profile/experience // @desc Add profile experience // @access Private router.put( "/experience", auth, check("title", "Title is required").notEmpty(), check("company", "Company is required").notEmpty(), check("from", "From date is required and needs to be from the past") .notEmpty() .custom((value, { req }) => (req.body.to ? value < req.body.to : true)), async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } try { if (isUserId(req)) { const profile = await Profile.findOne({ user: req.user.id }); if (profile) { profile.experience.unshift(req.body); await profile.save(); } res.json(profile); } } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); res.status(500).send("Server Error"); } }, ); // @route DELETE api/profile/experience/:exp_id // @desc Delete experience from profile // @access Private router.delete("/experience/:exp_id", auth, async (req, res) => { try { if (isUserId(req)) { const foundProfile = await Profile.findOne({ user: req.user.id }); if (foundProfile) { foundProfile.experience = foundProfile.experience.filter( (exp: any) => exp._id.toString() !== req.params.exp_id, ); await foundProfile.save(); } return res.status(200).json(foundProfile); } } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); return res.status(500).json({ msg: "Server error" }); } }); // @route PUT api/profile/education // @desc Add profile education // @access Private router.put( "/education", auth, check("school", "School is required").notEmpty(), check("degree", "Degree is required").notEmpty(), check("fieldofstudy", "Field of study is required").notEmpty(), check("from", "From date is required and needs to be from the past") .notEmpty() .custom((value, { req }) => (req.body.to ? value < req.body.to : true)), async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } try { if (isUserId(req)) { const profile = await Profile.findOne({ user: req.user.id }); if (profile) { profile.education.unshift(req.body); await profile.save(); } res.json(profile); } } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); res.status(500).send("Server Error"); } }, ); // @route DELETE api/profile/education/:edu_id // @desc Delete education from profile // @access Private router.delete("/education/:edu_id", auth, async (req, res) => { try { if (isUserId(req)) { const foundProfile = await Profile.findOne({ user: req.user.id }); if (foundProfile) { foundProfile.education = foundProfile.education.filter( (edu: any) => edu._id.toString() !== req.params.edu_id, ); await foundProfile.save(); } return res.status(200).json(foundProfile); } } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); return res.status(500).json({ msg: "Server error" }); } }); // @route GET api/profile/github/:username // @desc Get user repos from Github // @access Public router.get("/github/:username", async (req, res) => { console.log(config.get("githubToken")); console.log(req.params.username); try { const uri = encodeURI( `https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`, ); const headers = { "user-agent": "node.js", Authorization: `token ${config.get("githubToken")}`, }; const gitHubResponse = await axios.get(uri, { headers }); return res.json(gitHubResponse.data); } catch (err: unknown) { if (typeof err === "string") console.error(err); else if (err instanceof Error) console.error(err.message); return res.status(404).json({ msg: "No Github profile found" }); } }); module.exports = router;