-
Notifications
You must be signed in to change notification settings - Fork 590
Backend #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Backend #18
Changes from all commits
d6d24c5
0cc3f18
41463cd
9f3344c
0a107f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| MONGO_URI=mongodb://localhost:27017/digital-mess-card | ||
| JWT_SECRET=your-secret-key | ||
| PORT=5000 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||||||||||||||||||
| const connectDB = require('./config/db'); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const express= require('express'); | ||||||||||||||||||||||||||
| const cors = require('cors'); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const authRoutes = require('./routes/auth'); | ||||||||||||||||||||||||||
| const scanRoutes= require('./routes/scan'); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| app.use(corse()); | ||||||||||||||||||||||||||
| app.use(express.json()); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| connectDB(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| app.use('/api/auth', authRoutes); | ||||||||||||||||||||||||||
| app.use('/api/scan', scanRoutes); | ||||||||||||||||||||||||||
| require('dotenv').config(); | ||||||||||||||||||||||||||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Load environment variables before mounting routes. The +require('dotenv').config();
+
const connectDB = require('./config/db');
const express = require('express');
// ... rest of the imports
app.use('/api/auth', authRoutes);
app.use('/api/scan', scanRoutes);
-require('dotenv').config();📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| app.get('/',(req,res)=>{ | ||||||||||||||||||||||||||
| res.send('Digital MESS Card'); //Route | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const PORT= process.env.PORT || 5000; | ||||||||||||||||||||||||||
| app.listen(PORT, () => { | ||||||||||||||||||||||||||
| console.log(`Server running on http://localhost:${PORT}`); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,16 @@ | ||||||||||||||||||||||||||||||||||||
| const mongoose= require('mongoose'); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const connectDB = async () => { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| await mongoose.connect(process.env.MONGO_URI, { | ||||||||||||||||||||||||||||||||||||
| useNewUrlParser: true, | ||||||||||||||||||||||||||||||||||||
| useUnifiedTopology: true | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Load environment variables before accessing them. The +require('dotenv').config();
const mongoose= require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
| console.log('MongoDB connected'); | ||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||
| console.error('MongoDB connection error:', err); | ||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| module.exports = connectDB; | ||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,30 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bcrypt = require('bcryptjs'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const jwt = require('jsonwebtoken'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const User = require('../models/User'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { jwtSecret } = require('../config/auth'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exports.register = async (req, res) => { //to register new user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { username, password } = req.body; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hashedPassword = await bcrypt.hash(password, 10); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const user = new User({ username, password: hashedPassword }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await user.save(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(201).json({ message: 'User registered successfully' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(500).json({ error: 'Error registering user' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add input validation and duplicate username check. The registration function needs several security improvements:
Here's a suggested implementation: exports.register = async (req, res) => {
const { username, password } = req.body;
+ // Input validation
+ if (!username || !password) {
+ return res.status(400).json({ error: 'Username and password are required' });
+ }
+ if (password.length < 8) {
+ return res.status(400).json({ error: 'Password must be at least 8 characters long' });
+ }
try {
+ // Check for existing user
+ const existingUser = await User.findOne({ username });
+ if (existingUser) {
+ return res.status(400).json({ error: 'Username already exists' });
+ }
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ username, password: hashedPassword });
await user.save();
res.status(201).json({ message: 'User registered successfully' });
} catch (err) {
- res.status(500).json({ error: 'Error registering user' });
+ console.error('Registration error:', err);
+ res.status(500).json({ error: 'Internal server error during registration' });
}
}; |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exports.login = async (req, res) => { //Authenticate access | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { username, password } = req.body; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const user= await User.findOne({ username }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!user || !(await bcrypt.compare(password, user.password))) { // //to check whether the login details are valid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(400).json({ error: 'Invalid credentials' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token= jwt.sign({ userId: user._id }, jwtSecret, { expiresIn: '1h' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.json({ token }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(500).json({ error: 'Error logging in' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhance login security with rate limiting and better error handling. The login function needs several security improvements:
Here's a suggested implementation: +const rateLimit = require('express-rate-limit');
+
+// Rate limiting for failed login attempts
+const loginLimiter = rateLimit({
+ windowMs: 15 * 60 * 1000, // 15 minutes
+ max: 5 // limit each IP to 5 requests per windowMs
+});
+
exports.login = async (req, res) => {
const { username, password } = req.body;
+ // Input validation
+ if (!username || !password) {
+ return res.status(400).json({ error: 'Username and password are required' });
+ }
try {
const user = await User.findOne({ username });
if (!user || !(await bcrypt.compare(password, user.password))) {
+ // Log failed attempt
+ console.warn(`Failed login attempt for username: ${username}`);
return res.status(400).json({ error: 'Invalid credentials' });
}
- const token = jwt.sign({ userId: user._id }, jwtSecret, { expiresIn: '1h' });
+ const token = jwt.sign(
+ { userId: user._id },
+ jwtSecret,
+ { expiresIn: process.env.JWT_EXPIRATION || '1h' }
+ );
res.json({ token });
} catch (err) {
- res.status(500).json({ error: 'Error logging in' });
+ console.error('Login error:', err);
+ res.status(500).json({ error: 'Internal server error during login' });
}
};Additionally, consider implementing the following security measures:
📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| const Scan= require('../models/Scan'); | ||
|
|
||
| exports.scan= async (req, res) => { //to get info about user and the current time | ||
| const { type } = req.body; | ||
| const userId = req.userId; | ||
|
|
||
| const now = new Date(); | ||
| const hours = now.getHours(); | ||
| const minutes = now.getMinutes(); | ||
|
|
||
| if (type === 'lunch' && !(hours >= 11 && minutes >= 30 && hours < 14)) { //to check whether scanning of lunch time is valid | ||
| return res.status(400).json({ error: 'Lunch scanning is only allowed between 11:30 AM and 2:00 PM' }); | ||
| } | ||
|
|
||
| if (type === 'snack' && !(hours >= 15 && minutes >= 30 && hours < 17)) { //to check whether scanning of snack time is valid | ||
| return res.status(400).json({ error: 'Snack scanning is only allowed between 3:30 PM and 5:00 PM' }); | ||
| } | ||
|
Comment on lines
+11
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix time window comparison logic. The current time window checks are incorrect and may allow scans outside the intended windows. For example, at 11:00 with minutes >= 30, the lunch check would pass. - if (type === 'lunch' && !(hours >= 11 && minutes >= 30 && hours < 14)) {
+ const currentTime = hours * 60 + minutes;
+ const lunchStart = 11 * 60 + 30; // 11:30
+ const lunchEnd = 14 * 60; // 14:00
+ if (type === 'lunch' && (currentTime < lunchStart || currentTime >= lunchEnd)) {
return res.status(400).json({ error: 'Lunch scanning is only allowed between 11:30 AM and 2:00 PM' });
}
- if (type === 'snack' && !(hours >= 15 && minutes >= 30 && hours < 17)) {
+ const snackStart = 15 * 60 + 30; // 15:30
+ const snackEnd = 17 * 60; // 17:00
+ if (type === 'snack' && (currentTime < snackStart || currentTime >= snackEnd)) { |
||
|
|
||
| try { | ||
| const existingScan = await Scan.findOne({ userId, type, date: { $gte: new Date().setHours(0, 0, 0, 0) } }); | ||
| if (existingScan) { | ||
| return res.status(400).json({ error: 'Already scanned today' }); //to check whether a scanning already occured in the given time | ||
| } | ||
|
|
||
| const scan = new Scan({ userId, type }); | ||
| await scan.save(); //to save current scan | ||
| res.json({ message: 'Scan successful' }); | ||
| } catch (err) { | ||
| res.status(500).json({ error: 'Error processing scan' }); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const jwt= require('jsonwebtoken'); | ||
| const { jwtSecret }= require('../config/auth'); | ||
|
|
||
| exports.authenticate= (req,res,next) => { | ||
| const token=req.header('Authorization'); | ||
| if (!token) return res.status(401).json({ error: 'Access denied' }); | ||
|
|
||
| try{ | ||
| const decoded= jwt.verify(token, jwtSecret); | ||
| req.userId= decoded.userId; | ||
| next(); | ||
| } catch (err) { | ||
| res.status(400).json({ error: 'Invalid token' }); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| const mongoose= require('mongoose'); | ||
| const scanSchema =new mongoose.Schema({ //Scan date update in db | ||
| userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, | ||
| type: { type: String, enum: ['lunch', 'snack'], required: true }, | ||
| date :{ type: Date, default: Date.now}, | ||
| }); | ||
|
|
||
| module.exports= mongoose.model('Scan', scanSchema); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| const mongoose= require('mongoose'); | ||
|
|
||
| const userSchema= new mongoose.Schema({ //login details connected to db | ||
| username:{ type: String, required: true, unique: true }, | ||
| password:{ type: String, required: true, match:/^[a-zA-Z0-9]+$/, | ||
| } | ||
|
Comment on lines
+5
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strengthen password security. The current password validation is insufficient:
- password:{ type: String, required: true, match:/^[a-zA-Z0-9]+$/,
- }
+ password: {
+ type: String,
+ required: true,
+ minlength: 8,
+ match: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
+ }Also, add a pre-save hook to hash the password: const bcrypt = require('bcrypt');
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
}); |
||
| }); | ||
| module.exports= mongoose.model('User', userSchema); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| const express= require('express'); | ||
| const { register, login } = require('../controllers/authController'); | ||
|
|
||
| const router= express.Router(); | ||
|
|
||
| router.post('/register', register); | ||
| router.post('/login', login); | ||
|
|
||
| module.exports = router; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| const express= require('express'); | ||
| const { scan }= require('../controllers/scanController'); | ||
| const { authenticate }= require('../middleware/auth'); | ||
|
|
||
| const router = express.Router(); | ||
|
|
||
| router.post('/scan', authenticate, scan); | ||
|
|
||
| module.exports= router; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix CORS middleware typo and import.
The CORS middleware has a typo and is not imported.