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