Skip to content
Open
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
102 changes: 102 additions & 0 deletions backend/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// controllers/auth.controller.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');

// @desc Register a new user
// @route POST /api/auth/register
// @access Public
exports.register = async (req, res) => {
try {
const { name, email, password, role } = req.body;

// Basic validation
if (!name || !email || !password || !role) {
return res.status(400).json({ message: 'Please provide all required fields' });
}

// Check if user already exists
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ message: 'User already exists with this email' });
}

// Create new user (password will be hashed by the pre-save hook)
const user = await User.create({
name,
email,
password,
role,
profile: {} // initialize empty profile
});

// Generate JWT token (optional – you can also require login after registration)
const token = jwt.sign(
{ id: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);

// Return user info (excluding password) and token
res.status(201).json({
message: 'User registered successfully',
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role,
profile: user.profile
}
});
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error', error: error.message });
}
};

// @desc Login user
// @route POST /api/auth/login
// @access Public
exports.login = async (req, res) => {
try {
const { email, password } = req.body;

if (!email || !password) {
return res.status(400).json({ message: 'Please provide email and password' });
}

// Find user by email
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}

// Check password
const isMatch = await user.comparePassword(password);
if (!isMatch) {
return res.status(401).json({ message: 'Invalid credentials' });
}

// Generate token
const token = jwt.sign(
{ id: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);

res.json({
message: 'Login successful',
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role,
profile: user.profile
}
});
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error', error: error.message });
}
};
51 changes: 51 additions & 0 deletions backend/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// controllers/user.controller.js
const User = require('../models/User');

// @desc Get current user profile
// @route GET /api/users/profile
// @access Private
exports.getProfile = async (req, res) => {
try {
const user = await User.findById(req.user.id).select('-password');
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
};

// @desc Update current user profile
// @route PUT /api/users/profile
// @access Private
exports.updateProfile = async (req, res) => {
try {
const { bio, company, investmentHistory, preferences } = req.body;

const user = await User.findById(req.user.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}

// Update only fields that are sent in the request
if (bio !== undefined) user.profile.bio = bio;
if (company !== undefined) user.profile.company = company;
if (investmentHistory !== undefined) user.profile.investmentHistory = investmentHistory;
if (preferences) {
if (preferences.categories !== undefined) user.profile.preferences.categories = preferences.categories;
if (preferences.riskTolerance !== undefined) user.profile.preferences.riskTolerance = preferences.riskTolerance;
}

await user.save();

res.json({
message: 'Profile updated successfully',
profile: user.profile
});
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
};
27 changes: 27 additions & 0 deletions backend/middleware/auth.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// middleware/auth.middleware.js
const jwt = require('jsonwebtoken');

module.exports = (req, res, next) => {
try {
// Get token from Authorization header
const authHeader = req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Access denied. No token provided.' });
}

const token = authHeader.replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);

// Attach user info to request object
req.user = decoded; // { id, role, iat, exp }
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ message: 'Token expired' });
}
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ message: 'Invalid token' });
}
res.status(500).json({ message: 'Server error' });
}
};
14 changes: 14 additions & 0 deletions backend/middleware/role.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// middleware/role.middleware.js
const roleMiddleware = (...allowedRoles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ message: 'Unauthorized - no user found' });
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ message: 'Forbidden - insufficient role' });
}
next();
};
};

module.exports = roleMiddleware;
55 changes: 55 additions & 0 deletions backend/models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['investor', 'entrepreneur'],
required: true
},
profile: {
bio: { type: String, default: '' },
company: { type: String, default: '' },
investmentHistory: [{ type: String }], // or more complex structure
preferences: {
categories: [{ type: String }],
riskTolerance: { type: String, enum: ['low', 'medium', 'high'], default: 'medium' }
}
}
}, { timestamps: true });

// Hash password before saving
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (err) {
next(err);
}
});

// Method to compare passwords (for login)
userSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};

module.exports = mongoose.model('User', userSchema);
Loading