Added Code base from SeniorProject

This commit is contained in:
goldfishjonny 2025-04-22 21:43:12 -07:00
parent 39004bbc63
commit 06a43a69f4
12 changed files with 1852 additions and 0 deletions

32
app.js Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

1453
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
package.json Normal file
View 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"
}
}

View 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 });
}
};

View 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
View 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;

View 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
View 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
View File

View 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;

View 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;