Added Code base from SeniorProject
This commit is contained in:
parent
39004bbc63
commit
06a43a69f4
32
app.js
Normal file
32
app.js
Normal file
@ -0,0 +1,32 @@
|
||||
const express = require('express');
|
||||
// const mongoose = require('mongoose');
|
||||
const cors = require('cors');
|
||||
const userRoutes = require('./src/routes/v1/userRoutes');
|
||||
const loginRoutes = require('./src/routes/v1/authRoutes');
|
||||
|
||||
const app = express();
|
||||
|
||||
require('dotenv').config();
|
||||
const port = process.env.PORT || 5000;
|
||||
|
||||
const connectDB = require('./src/db/connectDB');
|
||||
connectDB();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!');
|
||||
});
|
||||
|
||||
app.use('/api/v1/users', userRoutes);
|
||||
app.use('/api/v1/login', loginRoutes);
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.status(404).json({ message: 'Route not found' });
|
||||
});
|
||||
|
||||
app.listen(port,'0.0.0.0', () => {
|
||||
console.log(`RevSocial is listening on port ${port}`);
|
||||
});
|
||||
BIN
assets/Placeholder.jpg
Normal file
BIN
assets/Placeholder.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
1453
package-lock.json
generated
Normal file
1453
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
Normal file
22
package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"bcrpyt": "^2.0.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^8.10.0",
|
||||
"nodemon": "^3.1.9"
|
||||
}
|
||||
}
|
||||
92
src/controllers/authController.js
Normal file
92
src/controllers/authController.js
Normal file
@ -0,0 +1,92 @@
|
||||
const User = require('../models/user');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// Login user
|
||||
exports.login = async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// 1️⃣ Check if user exists
|
||||
const user = await User.findOne({ username });
|
||||
if (!user) {
|
||||
return res.status(400).json({ message: 'Invalid email or password' });
|
||||
}
|
||||
|
||||
// 2️⃣ Compare passwords
|
||||
const isMatch = await bcrypt.compare(password, user.password);
|
||||
if (!isMatch) {
|
||||
return res.status(400).json({ message: 'Invalid email or password' });
|
||||
}
|
||||
|
||||
// 3️⃣ Generate JWT token
|
||||
const accessToken = jwt.sign(
|
||||
{ userId: user._id },
|
||||
process.env.JWT_SECRET || 'csc491_access_secret', // Use a secret key from env
|
||||
{ expiresIn: '1h' } // Token expires in 1 hour
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId: user._id },
|
||||
process.env.JWT_REFRESH || 'csc491_refresh_secret', // Use a secret key from env
|
||||
);
|
||||
|
||||
user.refresh_token = refreshToken;
|
||||
await user.save();
|
||||
|
||||
// 4️⃣ Send response with token
|
||||
res.status(200).json({
|
||||
message: 'Login successful',
|
||||
accessToken,
|
||||
refreshToken,
|
||||
user: { id: user._id, username: user.username, email: user.email }
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
exports.refreshToken = async (req, res) => {
|
||||
try {
|
||||
const { refreshToken } = req.body;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(400).json({ message: 'Refresh token is required' });
|
||||
}
|
||||
|
||||
jwt.verify(refreshToken, process.env.JWT_REFRESH || 'csc491_refresh_secret', async (err, u) => {
|
||||
if (err) {
|
||||
return res.status(403).json({ message: 'Invalid refresh token' });
|
||||
}
|
||||
|
||||
const user = await User.findById(u.userId);
|
||||
if (!u || u.refresh_token !== refreshToken) {
|
||||
return res.status(403).json({ message: 'User not found' });
|
||||
}
|
||||
|
||||
const newAccessToken = jwt.sign(
|
||||
{ userId: u._id },
|
||||
process.env.JWT_SECRET || 'csc491_access_secret', // Use a secret key from env
|
||||
{ expiresIn: '1h' } // Token expires in 1 hour
|
||||
);
|
||||
|
||||
res.status(200).json({ accessToken: newAccessToken });
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
exports.logout = async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.user;
|
||||
|
||||
await User.findByIdAndUpdate(userId, { refresh_token: '' });
|
||||
|
||||
res.status(200).json({ message: 'Logout successful' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Server error', error: error.message });
|
||||
}
|
||||
};
|
||||
114
src/controllers/userController.js
Normal file
114
src/controllers/userController.js
Normal file
@ -0,0 +1,114 @@
|
||||
const User = require('../models/user');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// Get single user by ID
|
||||
exports.getUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findById(req.params.id)
|
||||
.select('-password');
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
res.status(200).json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
message: 'Server error retrieving user',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create new user
|
||||
exports.register = async (req, res) => {
|
||||
try {
|
||||
const { email } = req.body;
|
||||
|
||||
// Check for existing user
|
||||
const existingUser = await User.findOne({ email });
|
||||
if (existingUser) {
|
||||
return res.status(400).json({ message: 'User already exists' });
|
||||
}
|
||||
|
||||
const newUser = new User(req.body);
|
||||
await newUser.save();
|
||||
|
||||
// Return user without password
|
||||
const userResponse = newUser.toObject();
|
||||
delete userResponse.password;
|
||||
|
||||
res.status(201).json(userResponse);
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
message: 'Validation failed',
|
||||
errors: error.errors ? Object.values(error.errors).map(e => e.message) : [error.message]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update user
|
||||
exports.updateUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByIdAndUpdate(
|
||||
req.params.id,
|
||||
req.body,
|
||||
{ new: true, runValidators: true }
|
||||
).select('-password');
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
|
||||
res.status(200).json(user);
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
message: 'Update failed',
|
||||
errors: error.errors ? Object.values(error.errors).map(e => e.message) : [error.message]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get all users (with pagination)
|
||||
exports.getAll = async (req, res) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 10;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const users = await User.find()
|
||||
.select('-password')
|
||||
.skip(skip)
|
||||
.limit(limit);
|
||||
|
||||
res.status(200).json({
|
||||
count: users.length,
|
||||
page,
|
||||
totalPages: Math.ceil(await User.countDocuments() / limit),
|
||||
users
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
message: 'Server error retrieving users',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.getProfile = async (req, res) => {
|
||||
try {
|
||||
console.log(req.user.userId);
|
||||
const user = await User.findById(req.user.userId)
|
||||
.select('username profile_pic number_of_cars number_of_friends garage posts');
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
res.status(200).json(user);
|
||||
} catch (error) {
|
||||
console.error('Error retrieving user:', error);
|
||||
res.status(500).json({
|
||||
message: 'Server error retrieving user',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
24
src/db/connectDB.js
Normal file
24
src/db/connectDB.js
Normal file
@ -0,0 +1,24 @@
|
||||
const mongoose = require('mongoose');
|
||||
require('dotenv').config();
|
||||
|
||||
const connectDB = async () => {
|
||||
try {
|
||||
const uri = process.env.URI;
|
||||
const dbName = process.env.DB_NAME;
|
||||
|
||||
if (!uri || !dbName) {
|
||||
throw new Error("MongoDB URI or DB_NAME is missing in .env");
|
||||
}
|
||||
|
||||
console.log('Connecting to database...');
|
||||
|
||||
await mongoose.connect(uri, { dbName });
|
||||
|
||||
console.log(`Connected to database: ${dbName}`);
|
||||
} catch (err) {
|
||||
console.error('Database connection failed:', err.message);
|
||||
process.exit(1); // Exit the process on connection failure
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = connectDB;
|
||||
19
src/middleware/authMiddleware.js
Normal file
19
src/middleware/authMiddleware.js
Normal file
@ -0,0 +1,19 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
module.exports = (req, res, next) => {
|
||||
console.log(req.headers);
|
||||
const authHeader = req.headers['authorization'];
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ message: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
|
||||
jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret', (err, decoded) => {
|
||||
if (err) {
|
||||
return res.status(403).json({ message: 'Invalid or expired token' });
|
||||
}
|
||||
req.user = decoded; // Attaching the decoded user data to the request object
|
||||
next();
|
||||
});
|
||||
};
|
||||
71
src/models/user.js
Normal file
71
src/models/user.js
Normal file
@ -0,0 +1,71 @@
|
||||
const { Schema, model } = require('mongoose');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
const userSchema = new Schema(
|
||||
{
|
||||
username: {
|
||||
type: String,
|
||||
required: [true, 'Username is required'],
|
||||
unique: true,
|
||||
trim: true,
|
||||
minlength: [3, 'Username must be at least 3 characters'],
|
||||
maxlength: [30, 'Username cannot exceed 30 characters'],
|
||||
},
|
||||
display_name: {
|
||||
type: String,
|
||||
default: function () {
|
||||
return this.username;
|
||||
},
|
||||
trim: true,
|
||||
maxlength: [30, 'Display name cannot exceed 30 characters'],
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: [true, 'Email is required'],
|
||||
unique: true,
|
||||
trim: true,
|
||||
lowercase: true,
|
||||
match: [
|
||||
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/,
|
||||
'Please enter a valid email address',
|
||||
],
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: [true, 'Password is required'],
|
||||
minlength: [6, 'Password must be at least 6 characters'],
|
||||
},
|
||||
profile_pic: {
|
||||
type: String,
|
||||
default: 'https://i.imgur.com/qhBcRtU.jpeg',
|
||||
},
|
||||
refresh_token: {
|
||||
type: String,
|
||||
},
|
||||
number_of_cars: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
number_of_friends: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
garage: {
|
||||
type: String,
|
||||
},
|
||||
posts: {
|
||||
type: String,
|
||||
}
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
// Hash password before saving
|
||||
userSchema.pre('save', async function (next) {
|
||||
if (!this.isModified('password')) return next();
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
this.password = await bcrypt.hash(this.password, salt);
|
||||
next();
|
||||
});
|
||||
|
||||
module.exports = model('User', userSchema);
|
||||
0
src/routes/index.js
Normal file
0
src/routes/index.js
Normal file
9
src/routes/v1/authRoutes.js
Normal file
9
src/routes/v1/authRoutes.js
Normal file
@ -0,0 +1,9 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {login, refreshToken} = require('../../controllers/authController');
|
||||
|
||||
router.post('/', login);
|
||||
|
||||
router.post('/refresh-token', refreshToken);
|
||||
|
||||
module.exports = router;
|
||||
16
src/routes/v1/userRoutes.js
Normal file
16
src/routes/v1/userRoutes.js
Normal file
@ -0,0 +1,16 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { getUser, register, updateUser, getAll, getProfile } = require('../../controllers/userController');
|
||||
const authMiddleware = require('../../middleware/authMiddleware');
|
||||
|
||||
router.get('/profile', authMiddleware, getProfile);
|
||||
|
||||
router.get('/:id', getUser);
|
||||
|
||||
router.get('/', getAll);
|
||||
|
||||
router.post('/', register);
|
||||
|
||||
router.put('/:id', updateUser);
|
||||
|
||||
module.exports = router;
|
||||
Loading…
x
Reference in New Issue
Block a user