Skip to content
Merged
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
34 changes: 30 additions & 4 deletions src/controllers/users.controller.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const User = require('../models/User.model');
const { sendSuccess } = require('../utils/response');

/**
Expand All @@ -7,11 +8,8 @@ const { sendSuccess } = require('../utils/response');
*/
const getCurrentUser = async (req, res, next) => {
try {
// The authenticate middleware already attaches the user to req.user
// We need to exclude sensitive fields before sending the response
const user = req.user.toObject();

// Remove sensitive fields
delete user.password;
delete user.refreshTokenHash;
delete user.resetPasswordToken;
Expand All @@ -23,6 +21,34 @@ const getCurrentUser = async (req, res, next) => {
}
};

/**
* Update current authenticated user's profile
* @route PATCH /api/users/me
* @access Private (requires authentication)
*/
const updateCurrentUser = async (req, res, next) => {
try {
const { fullName, walletAddress } = req.body;

// Build update object with only allowed fields
const allowedUpdates = {};
if (fullName !== undefined) allowedUpdates.fullName = fullName;
if (walletAddress !== undefined) allowedUpdates.walletAddress = walletAddress;

// Update and return the new document
const updatedUser = await User.findByIdAndUpdate(
req.userId,
{ $set: allowedUpdates },
{ new: true, runValidators: true }
).select('-password -refreshTokenHash -resetPasswordToken -emailVerificationToken');

return sendSuccess(res, updatedUser, 200, 'Profile updated successfully');
} catch (error) {
next(error);
}
};

module.exports = {
getCurrentUser,
};
updateCurrentUser,
};
34 changes: 14 additions & 20 deletions src/middlewares/validate.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
const validate = schema => {
return (req, res, next) => {
const { error } = schema.validate(req.body, { abortEarly: false });

if (error) {
const errors = error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message,
}));

return res.status(400).json({
success: false,
message: 'Validation failed',
errors,
});
}

next();
};
/**
* Validation middleware factory
* Validates request body against a Joi schema
*/
const validate = (schema) => (req, res, next) => {
const { error } = schema.validate(req.body, { abortEarly: false });
if (error) {
const err = new Error(error.details.map((d) => d.message).join(', '));
err.statusCode = 400;
err.isOperational = true;
return next(err);
}
next();
};

module.exports = validate;
module.exports = validate;
9 changes: 7 additions & 2 deletions src/routes/users.routes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
const express = require('express');
const authenticate = require('../middlewares/auth');
const { getCurrentUser } = require('../controllers/users.controller');
const { getCurrentUser, updateCurrentUser } = require('../controllers/users.controller');
const { updateProfileSchema } = require('../validators/auth.validators');
const validate = require('../middlewares/validate');

const router = express.Router();

// GET /api/users/me - Get current authenticated user's profile
router.get('/me', authenticate, getCurrentUser);

module.exports = router;
// PATCH /api/users/me - Update current authenticated user's profile
router.patch('/me', authenticate, validate(updateProfileSchema), updateCurrentUser);

module.exports = router;
18 changes: 17 additions & 1 deletion src/validators/auth.validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,27 @@ const changePasswordSchema = Joi.object({
}),
});

const updateProfileSchema = Joi.object({
fullName: Joi.string().min(2).max(100).messages({
'string.min': 'Full name must be at least 2 characters long',
'string.max': 'Full name cannot exceed 100 characters',
}),
walletAddress: Joi.string()
.pattern(/^G[A-Z2-7]{55}$/)
.messages({
'string.pattern.base':
'Wallet address must be a valid Stellar address (starts with G, 56 characters)',
}),
}).min(1).messages({
'object.min': 'At least one field (fullName or walletAddress) must be provided',
});

module.exports = {
registerSchema,
loginSchema,
resetPasswordSchema,
forgotPasswordSchema,
refreshTokenSchema,
changePasswordSchema,
};
updateProfileSchema,
};
Loading