Skip to content
Merged
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
Binary file added backend/.gitignore
Binary file not shown.
3 changes: 3 additions & 0 deletions backend/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ JWT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=
CLIENT_URL=
SESSION_SECRET=
ADMIN_EMAIL=
Expand Down
10 changes: 8 additions & 2 deletions backend/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ require('dotenv').config();
const JWT_SECRET = process.env.JWT_SECRET || 'devsync_secure_jwt_secret_key_for_authentication';

module.exports = function(req, res, next) {
// Get token from header
// Check if user is authenticated via Passport session (GitHub, Google)
if (req.isAuthenticated && req.isAuthenticated()) {
console.log('User authenticated via session:', req.user);
return next();
}

// Get token from header for JWT auth
const token = req.header('x-auth-token');

// Check if no token
if (!token) {
return res.status(401).json({ errors: [{ msg: 'No token, authorization denied' }] });
return res.status(401).json({ errors: [{ msg: 'No authentication, authorization denied' }] });
}

// Verify token
Expand Down
6 changes: 3 additions & 3 deletions backend/middleware/rateLimit/authLimiterMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { RateLimiterMemory } = require('rate-limiter-flexible');

exports.authLimiter = new RateLimiterMemory({
points: 5,
duration: 60,
blockDuration: 60 * 5,
points: 20, // Increased from 5 to 20 attempts
duration: 60, // Per minute
blockDuration: 60 * 2, // Reduced block time to 2 minutes
})
35 changes: 35 additions & 0 deletions backend/models/Feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FeedbackSchema = new Schema({
userId: {
type: String,
required: true
},
rating: {
type: Number,
required: true,
min: 1,
max: 5
},
comment: {
type: String,
required: true,
minlength: 10
},
category: {
type: String,
default: 'other',
enum: ['ui', 'features', 'bugs', 'suggestions', 'other']
},
isAnonymous: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
}
});

module.exports = mongoose.model('Feedback', FeedbackSchema);
7 changes: 6 additions & 1 deletion backend/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ const UserSchema = new Schema({
unique: true,
sparse: true, // multiple nulls allowed
},
githubId: {
type: String,
unique: true,
sparse: true, // multiple nulls allowed
},
name: {
type: String,
required: true,
Expand All @@ -29,7 +34,7 @@ const UserSchema = new Schema({
password: {
type: String,
required: function () {
return !this.googleId;
return !this.googleId && !this.githubId;
},
},
avatar: {
Expand Down
31 changes: 31 additions & 0 deletions backend/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,41 @@ router.get("/", auth, async (req, res) => {
// @access Private
router.get("/me", (req, res) => {
if (req.isAuthenticated()) {
console.log("User is authenticated via session:", req.user);
res.json(req.user);
} else {
res.status(401).json({ message: "Not logged in" });
}
});

// @route GET api/auth/check
// @desc Check if user is authenticated (works for both JWT and session auth)
// @access Public
router.get("/check", (req, res) => {
if (req.isAuthenticated()) {
return res.json({
isAuthenticated: true,
authMethod: 'session',
user: req.user
});
}

const token = req.header('x-auth-token');
if (token) {
try {
const JWT_SECRET = process.env.JWT_SECRET || 'devsync_secure_jwt_secret_key_for_authentication';
const decoded = jwt.verify(token, JWT_SECRET);
return res.json({
isAuthenticated: true,
authMethod: 'token',
user: decoded.user
});
} catch (err) {
console.error('Token verification error:', err.message);
}
}

res.json({ isAuthenticated: false });
});

module.exports = router;
116 changes: 116 additions & 0 deletions backend/routes/feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Feedback = require('../models/Feedback');

// @route POST api/feedback
// @desc Submit user feedback
// @access Private
router.post('/', auth, async (req, res) => {
try {
const { rating, comment, category, isAnonymous } = req.body;

// Validate the data
if (!rating || rating < 1 || rating > 5) {
return res.status(400).json({ message: 'Please provide a valid rating between 1 and 5' });
}

if (!comment || comment.trim().length < 10) {
return res.status(400).json({ message: 'Please provide feedback with at least 10 characters' });
}

// Create a new feedback instance
const newFeedback = new Feedback({
userId: req.user.id,
rating,
comment,
category: category || 'other',
isAnonymous: isAnonymous || false
});

// Save the feedback to the database
await newFeedback.save();

res.json({ success: true, message: 'Feedback submitted successfully' });
} catch (err) {
console.error('Error submitting feedback:', err.message);
res.status(500).json({ message: 'Server error' });
}
});

// @route POST api/feedback/guest
// @desc Submit guest (unauthenticated) feedback
// @access Public
router.post('/guest', async (req, res) => {
try {
const { rating, comment, category, isAnonymous = true } = req.body;

// Validate the data
if (!rating || rating < 1 || rating > 5) {
return res.status(400).json({ message: 'Please provide a valid rating between 1 and 5' });
}

if (!comment || comment.trim().length < 10) {
return res.status(400).json({ message: 'Please provide feedback with at least 10 characters' });
}

// Create a new feedback instance for guest user
const newFeedback = new Feedback({
userId: "guest",
rating,
comment,
category: category || 'other',
isAnonymous: true // Always anonymous for guests
});

// Save the feedback to the database
await newFeedback.save();

res.json({ success: true, message: 'Guest feedback submitted successfully' });
} catch (err) {
console.error('Error submitting guest feedback:', err.message);
res.status(500).json({ message: 'Server error' });
}
});

// @route GET api/feedback
// @desc Get all feedback (for admin/community page)
// @access Public
router.get('/', async (req, res) => {
try {
console.log("Feedback GET request received");

// Get feedback sorted by date (newest first)
try {
const feedbackList = await Feedback.find()
.sort({ date: -1 })
.select(
// Don't include user ID if feedback is anonymous
'-__v ' + (req.query.includePrivate === 'true' ? '' : '-userId')
);

// Process feedback for public display
const processedFeedback = feedbackList.map(feedback => {
const feedbackObj = feedback.toObject();

// If feedback is anonymous, remove any identifiable information
if (feedbackObj.isAnonymous && !req.query.includePrivate) {
feedbackObj.userId = 'anonymous';
}

return feedbackObj;
});

return res.json(processedFeedback);
} catch (dbError) {
console.error("Database error:", dbError);
// If database operation fails, return empty array
return res.json([]);
}
} catch (err) {
console.error('Error getting feedback:', err.message);
res.status(500).json({ message: 'Server error' });
}
});

module.exports = router;
Loading