/**
* Profile API routes module.
* @module routes/api/profile
* @requires express
* @requires middleware/auth
* @requires express-validator
* @requires models/Profile
* @requires models/User
*
* @example
* // GET api/profile/me
* // Usage:
* // 1. Include auth middleware in your express app
* const auth = require('../../middleware/auth');
*
* // 2. Add the following route in your express app
* app.get('/api/profile/me', auth, async (req, res) => {
* // Your route handler logic
* });
*
* // 3. Send a GET request with the token in the 'x-auth-token' header
* // e.g., using axios:
* const axios = require('axios');
*
* axios.get('http://localhost:5000/api/profile/me', {
* headers: {
* 'x-auth-token': 'your_token_here'
* }
* }).then(response => {
* console.log(response.data); // User's profile
* }).catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // POST api/profile
* // Usage:
* // 1. Include auth middleware in your express app
* const auth = require('../../middleware/auth');
*
* // 2. Add the following route in your express app
* app.post('/api/profile', auth, async (req, res) => {
* // Your route handler logic
* });
*
* // 3. Send a POST request with the token in the 'x-auth-token' header
* // e.g., using axios:
* const axios = require('axios');
*
* axios.post('http://localhost:5000/api/profile', {
* headers: {
* 'x-auth-token': 'your_token_here'
* },
* data: {
* // Your profile data
* }
* }).then(response => {
* console.log(response.data); // Created or updated profile
* }).catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // GET api/profile/user/:user_id
* // Usage:
* // 1. Add the following route in your express app
* app.get('/api/profile/user/:user_id', checkObjectId('user_id'), async (req, res) => {
* // Your route handler logic
* });
*
* // 2. Send a GET request
* // e.g., using axios:
* const axios = require('axios');
*
* axios.get('http://localhost:5000/api/profile/user/12345')
* .then(response => {
* console.log(response.data); // Profile object
* })
* .catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // DELETE api/profile
* // Usage:
* // 1. Add the following route in your express app
* app.delete('/api/profile', auth, async (req, res) => {
* // Your route handler logic
* });
*
* // 2. Send a DELETE request
* // e.g., using axios:
* const axios = require('axios');
* const token = 'your_auth_token'; // Replace with the user's auth token
*
* axios.delete('http://localhost:5000/api/profile', {
* headers: { 'x-auth-token': token },
* })
* .then(response => {
* console.log(response.data); // { msg: 'User deleted' }
* })
* .catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // PUT api/profile/experience
* // Usage:
* // 1. Add the following route in your express app
* app.put('/api/profile/experience', auth, check('title', 'Title is required').notEmpty(), ...);
*
* // 2. Send a PUT request with experience data
* // e.g., using axios:
* const axios = require('axios');
* const token = 'your_auth_token'; // Replace with the user's auth token
* const experienceData = {
* title: 'Software Engineer',
* company: 'Awesome Company',
* from: '2020-05-01'
* };
*
* axios.put('http://localhost:5000/api/profile/experience', experienceData, {
* headers: { 'x-auth-token': token },
* })
* .then(response => {
* console.log(response.data); // Updated profile
* })
* .catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // DELETE api/profile/experience/:exp_id
* // Usage:
* // 1. Add the following route in your express app
* app.delete('/api/profile/experience/:exp_id', auth, async (req, res) => { ... });
*
* // 2. Send a DELETE request with the experience ID to delete
* // e.g., using axios:
* const axios = require('axios');
* const token = 'your_auth_token'; // Replace with the user's auth token
* const experienceId = 'your_experience_id'; // Replace with the experience ID to delete
*
* axios.delete(`http://localhost:5000/api/profile/experience/${experienceId}`, {
* headers: { 'x-auth-token': token },
* })
* .then(response => {
* console.log(response.data); // Updated profile
* })
* .catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // PUT api/profile/education
* // Usage:
* // 1. Add the following route in your express app
* app.put('/api/profile/education', auth, check(...), async (req, res) => { ... });
*
* // 2. Send a PUT request with the education data to add
* // e.g., using axios:
* const axios = require('axios');
* const token = 'your_auth_token'; // Replace with the user's auth token
* const educationData = {
* school: 'Example University',
* degree: 'Bachelor of Science',
* fieldofstudy: 'Computer Science',
* from: '2016-08-01',
* to: '2020-05-01',
* };
*
* axios.put('http://localhost:5000/api/profile/education', educationData, {
* headers: { 'x-auth-token': token },
* })
* .then(response => {
* console.log(response.data); // Updated profile
* })
* .catch(error => {
* console.error('Error:', error);
* });
*
* @example
* // DELETE api/profile/education/:edu_id
* // Usage:
* // 1. Add the following route in your express app
* app.delete('/api/profile/education/:edu_id', auth, async (req, res) => { ... });
*
* // 2. Send a DELETE request with the education ID to delete
* // e.g., using axios:
* const axios = require('axios');
* const token = 'your_auth_token'; // Replace with the user's auth token
* const educationId = '5d713995b721c3bb38c1f5d0'; // Replace with the education ID to delete
*
* axios.delete(`http://localhost:5000/api/profile/education/${educationId}`, {
* headers: { 'x-auth-token': token },
* })
* .then(response => {
* console.log(response.data); // Updated profile
* })
* .catch(error => {
* console.error('Error:', error);
* });
*/
const express = require('express');
const router = express.Router();
const auth = require('../../middleware/auth');
const { check, validationResult } = require('express-validator');
//const axios = require('axios');
//const config = require('config');
const normalize = require('normalize-url');
const checkObjectId = require('../../middleware/checkObjectId');
const Profile = require('../../models/Profile');
const User = require('../../models/User');
const Post = require('../../models/Post');
/**
* @route GET api/profile/me
* @desc Get current user's profile
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the user's profile or an error message
* @throws {Error} - If there's a server error
*/
router.get('/me', auth, async (req, res) => {
try {
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) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
/**
* @route POST api/profile
* @desc Create or update a user profile
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the created or updated profile or an error message
* @throws {Error} - If there's a server error
*/
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() });
}
const {
website,
skills,
youtube,
twitter,
instagram,
linkedin,
facebook,
...rest
} = req.body;
// Build profile object
const profileFields = {
user: req.user.id,
website:
website && website !== ''
? normalize(website, { forceHttps: true })
: '',
skills: Array.isArray(skills)
? skills
: skills.split(',').map((skill) => ' ' + skill.trim()),
...rest
};
// Build social fields object
const socialFields = { 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) {
console.error(err.message);
return res.status(500).send('Server Error');
}
}
);
/**
* GET endpoint to fetch all profiles.
*
* @route GET /
* @param {Object} req - Express request object.
* @param {Object} res - Express response object.
* @returns {Array<Object>} Returns an array of profiles with the associated user's name and avatar, or an error message with the appropriate status code.
* @throws {Error} Will return a 500 status if there is a server error.
*/
router.get('/', async (req, res) => {
try {
const profiles = await Profile.find().populate('user', ['name', 'avatar']);
res.json(profiles);
} catch (err) {
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
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the user's profile or an error message
* @throws {Error} - If there's a server error or invalid user ID
*/
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) {
console.error(err.message);
return res.status(500).json({ msg: 'Server error' });
}
}
);
/**
* @route DELETE api/profile
* @desc Delete profile, user & posts
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response with a success message
* @throws {Error} - If there's a server error
*/
router.delete('/', auth, async (req, res) => {
try {
// Remove user posts
// Remove profile
// Remove user
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) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
/**
* @route PUT api/profile/experience
* @desc Add profile experience
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the updated profile or an error message
* @throws {Error} - If there's a server error
*/
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 {
const profile = await Profile.findOne({ user: req.user.id });
profile.experience.unshift(req.body);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
/**
* @route DELETE api/profile/experience/:exp_id
* @desc Delete experience from profile
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the updated profile or an error message
* @throws {Error} - If there's a server error
*/
router.delete('/experience/:exp_id', auth, async (req, res) => {
try {
const foundProfile = await Profile.findOne({ user: req.user.id });
foundProfile.experience = foundProfile.experience.filter(
(exp) => exp._id.toString() !== req.params.exp_id
);
await foundProfile.save();
return res.status(200).json(foundProfile);
} catch (error) {
console.error(error);
return res.status(500).json({ msg: 'Server error' });
}
});
/**
* @route PUT api/profile/education
* @desc Add profile education
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the updated profile or an error message
* @throws {Error} - If there's a server error
*/
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 {
const profile = await Profile.findOne({ user: req.user.id });
profile.education.unshift(req.body);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
/**
* @route DELETE api/profile/education/:edu_id
* @desc Delete education from profile
* @access Private
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - JSON response containing the updated profile or an error message
* @throws {Error} - If there's a server error
*/
router.delete('/education/:edu_id', auth, async (req, res) => {
try {
const foundProfile = await Profile.findOne({ user: req.user.id });
foundProfile.education = foundProfile.education.filter(
(edu) => edu._id.toString() !== req.params.edu_id
);
await foundProfile.save();
return res.status(200).json(foundProfile);
} catch (error) {
console.error(error);
return res.status(500).json({ msg: 'Server error' });
}
});
/**
* Profile API router.
* @type {express.Router}
*/
module.exports = router;