/tests/setup.js']
};
\ No newline at end of file
diff --git a/server/routes/users.js b/server/routes/users.js
index cbc1bb3..31b48c0 100644
--- a/server/routes/users.js
+++ b/server/routes/users.js
@@ -7,7 +7,7 @@ const auth = require('../middleware/auth');
const searchLimiter = rateLimit({
windowMs: 60 * 1000,
- max: 30,
+ max: 60,
standardHeaders: true,
legacyHeaders: false,
message: { message: 'Too many requests, please try again later.' },
From 1b0b6e6074f942c27ed963944ab2eba84a12f1e0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 1 Apr 2026 18:23:41 +0000
Subject: [PATCH 7/8] fix: code quality - lint errors, dead code, security,
error logging, owner comparison bug
Agent-Logs-Url: https://github.com/Karanpratap7/DevLinkUp/sessions/36781746-e387-4359-b941-7fb7a644f608
Co-authored-by: Karanpratap7 <137878890+Karanpratap7@users.noreply.github.com>
---
client/src/components/Navbar.jsx | 1 -
client/src/components/PrivateRoute.jsx | 2 +-
client/src/components/ProfileForm.jsx | 2 +-
client/src/components/ProtectedRoute.jsx | 14 -------
.../__tests__/DeveloperCard.test.jsx | 2 +-
client/src/contexts/AuthContext.jsx | 10 +----
.../contexts/__tests__/AuthContext.test.jsx | 7 +---
client/src/pages/Discover.jsx | 2 -
client/src/pages/Profile.jsx | 38 ++-----------------
client/src/pages/ProjectDetail.jsx | 8 ++--
client/src/pages/__tests__/Discover.test.jsx | 2 +-
client/src/pages/__tests__/Profile.test.jsx | 12 ------
server/controllers/authController.js | 19 ++--------
server/controllers/projectController.js | 4 +-
server/controllers/userController.js | 6 ++-
server/models/User.js | 12 +-----
server/server.js | 22 ++---------
17 files changed, 28 insertions(+), 135 deletions(-)
delete mode 100644 client/src/components/ProtectedRoute.jsx
diff --git a/client/src/components/Navbar.jsx b/client/src/components/Navbar.jsx
index 3208c78..e7b798e 100644
--- a/client/src/components/Navbar.jsx
+++ b/client/src/components/Navbar.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
diff --git a/client/src/components/PrivateRoute.jsx b/client/src/components/PrivateRoute.jsx
index bf0a837..22ec77a 100644
--- a/client/src/components/PrivateRoute.jsx
+++ b/client/src/components/PrivateRoute.jsx
@@ -15,7 +15,7 @@ export default function PrivateRoute({ children }) {
}
// If not loading and no user, redirect to login
- if (!loading && !currentUser) {
+ if (!currentUser) {
// Save the attempted url for redirecting after login
return ;
}
diff --git a/client/src/components/ProfileForm.jsx b/client/src/components/ProfileForm.jsx
index 6370d5a..367ec0c 100644
--- a/client/src/components/ProfileForm.jsx
+++ b/client/src/components/ProfileForm.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { userAPI } from '../services/api';
diff --git a/client/src/components/ProtectedRoute.jsx b/client/src/components/ProtectedRoute.jsx
deleted file mode 100644
index 40f9f59..0000000
--- a/client/src/components/ProtectedRoute.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Navigate, useLocation } from 'react-router-dom';
-import { useAuth } from '../contexts/AuthContext';
-
-export default function ProtectedRoute({ children }) {
- const { currentUser } = useAuth();
- const location = useLocation();
-
- if (!currentUser) {
- // Redirect to login page but save the attempted url
- return ;
- }
-
- return children;
-}
\ No newline at end of file
diff --git a/client/src/components/__tests__/DeveloperCard.test.jsx b/client/src/components/__tests__/DeveloperCard.test.jsx
index c2f0301..f6a9311 100644
--- a/client/src/components/__tests__/DeveloperCard.test.jsx
+++ b/client/src/components/__tests__/DeveloperCard.test.jsx
@@ -1,4 +1,4 @@
-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import DeveloperCard from '../DeveloperCard';
diff --git a/client/src/contexts/AuthContext.jsx b/client/src/contexts/AuthContext.jsx
index 1a1b9f5..47a609d 100644
--- a/client/src/contexts/AuthContext.jsx
+++ b/client/src/contexts/AuthContext.jsx
@@ -5,6 +5,7 @@ const API_URL = (import.meta.env.VITE_API_URL || 'http://localhost:5001').replac
const AuthContext = createContext();
+// eslint-disable-next-line react-refresh/only-export-components
export function useAuth() {
return useContext(AuthContext);
}
@@ -16,8 +17,6 @@ export function AuthProvider({ children }) {
useEffect(() => {
const token = localStorage.getItem('token');
- // eslint-disable-next-line no-console
- console.log('[AuthContext] Initializing with token:', !!token);
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
fetchUser();
@@ -28,15 +27,10 @@ export function AuthProvider({ children }) {
const fetchUser = async () => {
try {
- // eslint-disable-next-line no-console
- console.log('[AuthContext] Fetching user data...');
const response = await axios.get(`${API_URL}/api/auth/me`);
- // eslint-disable-next-line no-console
- console.log('[AuthContext] User data received:', response.data);
setCurrentUser(response.data);
} catch (error) {
- // eslint-disable-next-line no-console
- console.error('[AuthContext] Error fetching user:', error);
+ console.error('[AuthContext] Failed to restore session:', error);
localStorage.removeItem('token');
delete axios.defaults.headers.common['Authorization'];
setCurrentUser(null);
diff --git a/client/src/contexts/__tests__/AuthContext.test.jsx b/client/src/contexts/__tests__/AuthContext.test.jsx
index 68fdd67..5ea53e7 100644
--- a/client/src/contexts/__tests__/AuthContext.test.jsx
+++ b/client/src/contexts/__tests__/AuthContext.test.jsx
@@ -239,11 +239,8 @@ describe('AuthContext', () => {
await waitFor(() =>
expect(screen.getByTestId('loading').textContent).toBe('false')
);
- const { login } = screen.getByTestId('login-btn').__reactFiber
- ? {} // not accessible this way
- : {};
- // Verify by calling login directly through a test component
- // (covered by Login.test.jsx which mocks useAuth — see note below)
+ // re-throws so the Login page can show its own error message
+ // (covered by Login.test.jsx which mocks useAuth)
});
});
diff --git a/client/src/pages/Discover.jsx b/client/src/pages/Discover.jsx
index e1bbe0a..be78348 100644
--- a/client/src/pages/Discover.jsx
+++ b/client/src/pages/Discover.jsx
@@ -31,12 +31,10 @@ export default function Discover() {
setLoading(true);
setError('');
try {
- console.log('[Discover] Fetching data...');
const [usersRes, projectsRes] = await Promise.all([
userAPI.getAllUsers(),
projectAPI.getAllProjects(),
]);
- console.log('[Discover] Data received:', { users: usersRes.data, projects: projectsRes.data });
setDevelopers(Array.isArray(usersRes.data) ? usersRes.data : []);
setProjects(Array.isArray(projectsRes.data) ? projectsRes.data : []);
} catch (err) {
diff --git a/client/src/pages/Profile.jsx b/client/src/pages/Profile.jsx
index 7980bff..eec4a71 100644
--- a/client/src/pages/Profile.jsx
+++ b/client/src/pages/Profile.jsx
@@ -13,50 +13,23 @@ export default function Profile() {
const [error, setError] = useState('');
useEffect(() => {
- // Debug output
- // eslint-disable-next-line no-console
- console.log('[Profile] useEffect', {
- id,
- currentUser,
- authLoading,
- hasToken: !!localStorage.getItem('token'),
- currentUserId: currentUser?._id
- });
const fetchProfileAndProjects = async () => {
if (authLoading) {
- // eslint-disable-next-line no-console
- console.log('[Profile] authLoading true, returning');
return;
}
setLoading(true);
setError('');
try {
- // Wait for auth to be ready
if (!currentUser && !id) {
- // eslint-disable-next-line no-console
- console.log('[Profile] No currentUser and no id, waiting for auth...');
return;
}
const userId = id || currentUser?._id;
- // eslint-disable-next-line no-console
- console.log('[Profile] Attempting to fetch with userId:', userId);
if (!userId) {
- // eslint-disable-next-line no-console
- console.log('[Profile] No userId available. Current state:', {
- id,
- currentUser,
- authLoading,
- hasToken: !!localStorage.getItem('token')
- });
setError('Please log in to view your profile or provide a valid user ID');
setLoading(false);
return;
}
- // eslint-disable-next-line no-console
- console.log('[Profile] Fetching user', userId);
const profileRes = await userAPI.getUser(userId);
- // eslint-disable-next-line no-console
- console.log('[Profile] profileRes', profileRes);
if (!profileRes.data) {
if (!currentUser) {
navigate('/login');
@@ -66,30 +39,25 @@ export default function Profile() {
}
setProfile(profileRes.data);
try {
- // eslint-disable-next-line no-console
- console.log('[Profile] Fetching projects for', userId);
const projectsRes = await projectAPI.getUserProjects(userId);
- // eslint-disable-next-line no-console
- console.log('[Profile] projectsRes', projectsRes);
setProjects(Array.isArray(projectsRes.data) ? projectsRes.data : []);
} catch (projectsErr) {
- // eslint-disable-next-line no-console
console.error('[Profile] Error fetching projects:', projectsErr);
setProjects([]);
}
} catch (err) {
- // eslint-disable-next-line no-console
console.error('[Profile] Error fetching profile data:', err);
setError(err.response?.data?.message || err.message || 'Failed to fetch profile or projects');
setProfile(null);
setProjects([]);
} finally {
setLoading(false);
- // eslint-disable-next-line no-console
- console.log('[Profile] setLoading(false)');
}
};
fetchProfileAndProjects();
+ // Using currentUser?._id (primitive) instead of currentUser (object) to avoid infinite
+ // re-renders caused by a new object reference on every render.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, currentUser?._id, authLoading, navigate]);
// Show loading state while auth is loading
diff --git a/client/src/pages/ProjectDetail.jsx b/client/src/pages/ProjectDetail.jsx
index b4785ce..03acfeb 100644
--- a/client/src/pages/ProjectDetail.jsx
+++ b/client/src/pages/ProjectDetail.jsx
@@ -22,15 +22,13 @@ export default function ProjectDetail() {
setLoading(true);
setError('');
try {
- console.log('[ProjectDetail] Fetching project:', id);
const response = await projectAPI.getProject(id);
- console.log('[ProjectDetail] Project data:', response.data);
if (!response.data) {
throw new Error('No project data received');
}
setProject(response.data);
} catch (err) {
- console.error('[ProjectDetail] Error:', err);
+ console.error('[ProjectDetail] Error fetching project:', err);
setError(
err.response?.data?.message ||
err.message ||
@@ -118,7 +116,9 @@ export default function ProjectDetail() {
);
}
- const isOwner = currentUser && currentUser._id === project.owner;
+ // project.owner may be a populated object or a plain string/ObjectId depending on context
+ const ownerId = project.owner?._id ?? project.owner;
+ const isOwner = currentUser && ownerId?.toString() === currentUser._id?.toString();
return (
diff --git a/client/src/pages/__tests__/Discover.test.jsx b/client/src/pages/__tests__/Discover.test.jsx
index 3c37050..f3a69eb 100644
--- a/client/src/pages/__tests__/Discover.test.jsx
+++ b/client/src/pages/__tests__/Discover.test.jsx
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { render, screen, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Discover from '../Discover';
diff --git a/client/src/pages/__tests__/Profile.test.jsx b/client/src/pages/__tests__/Profile.test.jsx
index 86b8a52..f3654aa 100644
--- a/client/src/pages/__tests__/Profile.test.jsx
+++ b/client/src/pages/__tests__/Profile.test.jsx
@@ -100,9 +100,6 @@ describe('Profile Component', () => {
logout: vi.fn(),
});
renderProfile();
- // Debug output
- // eslint-disable-next-line no-console
- console.log(document.body.innerHTML);
// Use findByText for async rendering
expect(await screen.findByText('Test User')).toBeInTheDocument();
expect(await screen.findByText('test@example.com')).toBeInTheDocument();
@@ -118,9 +115,6 @@ describe('Profile Component', () => {
logout: vi.fn(),
});
renderProfile();
- // Debug output
- // eslint-disable-next-line no-console
- console.log(document.body.innerHTML);
// Use findAllByText with a function matcher for robustness (skills appear in both Skills and project techStack)
expect((await screen.findAllByText((content) => content.includes('React'))).length).toBeGreaterThan(0);
expect((await screen.findAllByText((content) => content.includes('Node.js'))).length).toBeGreaterThan(0);
@@ -135,9 +129,6 @@ describe('Profile Component', () => {
logout: vi.fn(),
});
renderProfile();
- // Debug output
- // eslint-disable-next-line no-console
- console.log(document.body.innerHTML);
// Use findByText with a function matcher for robustness
expect(await screen.findByText((content) => content.includes('Test Project'))).toBeInTheDocument();
expect(await screen.findByText((content) => content.includes('Test description'))).toBeInTheDocument();
@@ -176,9 +167,6 @@ describe('Profile Component', () => {
});
projectAPI.getUserProjects.mockResolvedValue({ data: [] });
renderProfile();
- // Debug output
- // eslint-disable-next-line no-console
- console.log(document.body.innerHTML);
await waitFor(() => {
expect(screen.getByText('No projects found')).toBeInTheDocument();
});
diff --git a/server/controllers/authController.js b/server/controllers/authController.js
index d643da6..24a9296 100644
--- a/server/controllers/authController.js
+++ b/server/controllers/authController.js
@@ -10,10 +10,8 @@ const generateToken = (userId) => {
// Register new user
exports.signup = async (req, res) => {
try {
- console.log('Signup request received:', { body: req.body });
const errors = validationResult(req);
if (!errors.isEmpty()) {
- console.log('Validation errors:', errors.array());
return res.status(400).json({ errors: errors.array() });
}
@@ -22,7 +20,6 @@ exports.signup = async (req, res) => {
// Check if user already exists
const existingUser = await User.findOne({ email });
if (existingUser) {
- console.log('User already exists:', email);
return res.status(400).json({ message: 'User already exists' });
}
@@ -34,7 +31,6 @@ exports.signup = async (req, res) => {
});
await user.save();
- console.log('New user created:', { id: user._id, email: user.email });
// Generate token
const token = generateToken(user._id);
@@ -49,17 +45,15 @@ exports.signup = async (req, res) => {
});
} catch (error) {
console.error('Signup error:', error);
- res.status(500).json({ message: 'Server error', error: error.message });
+ res.status(500).json({ message: 'Server error' });
}
};
// Login user
exports.login = async (req, res) => {
try {
- console.log('Login request received:', { body: req.body });
const errors = validationResult(req);
if (!errors.isEmpty()) {
- console.log('Validation errors:', errors.array());
return res.status(400).json({ errors: errors.array() });
}
@@ -68,19 +62,15 @@ exports.login = async (req, res) => {
// Find user
const user = await User.findOne({ email });
if (!user) {
- console.log('User not found:', email);
return res.status(400).json({ message: 'Invalid credentials' });
}
// Check password
const isMatch = await user.comparePassword(password);
if (!isMatch) {
- console.log('Invalid password for user:', email);
return res.status(400).json({ message: 'Invalid credentials' });
}
- console.log('User logged in successfully:', { id: user._id, email: user.email });
-
// Generate token
const token = generateToken(user._id);
@@ -94,23 +84,20 @@ exports.login = async (req, res) => {
});
} catch (error) {
console.error('Login error:', error);
- res.status(500).json({ message: 'Server error', error: error.message });
+ res.status(500).json({ message: 'Server error' });
}
};
// Get current user
exports.getCurrentUser = async (req, res) => {
try {
- console.log('Get current user request:', { userId: req.user._id });
const user = await User.findById(req.user._id).select('-password');
if (!user) {
- console.log('User not found:', req.user._id);
return res.status(404).json({ message: 'User not found' });
}
- console.log('Current user retrieved:', { id: user._id, email: user.email });
res.status(200).json(user);
} catch (error) {
console.error('Get current user error:', error);
- res.status(500).json({ message: 'Server error', error: error.message });
+ res.status(500).json({ message: 'Server error' });
}
};
\ No newline at end of file
diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js
index c381d9a..1ac21b9 100644
--- a/server/controllers/projectController.js
+++ b/server/controllers/projectController.js
@@ -140,11 +140,10 @@ exports.getUserProjects = async (req, res) => {
.sort({ createdAt: -1 });
res.json(projects);
} catch (error) {
+ console.error('Get user projects error:', error);
res.status(500).json({ message: 'Server error' });
}
};
-
-// Search projects by tech stack
exports.searchProjectsByTech = async (req, res) => {
try {
const { tech } = req.query;
@@ -161,6 +160,7 @@ exports.searchProjectsByTech = async (req, res) => {
res.json(projects);
} catch (error) {
+ console.error('Search projects by tech error:', error);
res.status(500).json({ message: 'Server error' });
}
};
\ No newline at end of file
diff --git a/server/controllers/userController.js b/server/controllers/userController.js
index eab4a44..5c3ad14 100644
--- a/server/controllers/userController.js
+++ b/server/controllers/userController.js
@@ -7,6 +7,7 @@ exports.getAllUsers = async (req, res) => {
const users = await User.find().select('-password');
res.json(users);
} catch (error) {
+ console.error('Get all users error:', error);
res.status(500).json({ message: 'Server error' });
}
};
@@ -20,6 +21,7 @@ exports.getUserById = async (req, res) => {
}
res.json(user);
} catch (error) {
+ console.error('Get user by ID error:', error);
res.status(500).json({ message: 'Server error' });
}
};
@@ -52,11 +54,10 @@ exports.updateProfile = async (req, res) => {
res.json(user);
} catch (error) {
+ console.error('Update profile error:', error);
res.status(500).json({ message: 'Server error' });
}
};
-
-// Search users by skills
exports.searchUsersBySkills = async (req, res) => {
try {
const { skills } = req.query;
@@ -71,6 +72,7 @@ exports.searchUsersBySkills = async (req, res) => {
res.json(users);
} catch (error) {
+ console.error('Search users by skills error:', error);
res.status(500).json({ message: 'Server error' });
}
};
\ No newline at end of file
diff --git a/server/models/User.js b/server/models/User.js
index 72ef3a0..8056f06 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -51,27 +51,17 @@ userSchema.pre('save', async function(next) {
return next();
}
try {
- console.log('Hashing password for user:', this.email);
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
- console.error('Error hashing password:', error);
next(error);
}
});
// Method to compare password
userSchema.methods.comparePassword = async function(candidatePassword) {
- try {
- console.log('Comparing password for user:', this.email);
- const isMatch = await bcrypt.compare(candidatePassword, this.password);
- console.log('Password match result:', isMatch);
- return isMatch;
- } catch (error) {
- console.error('Error comparing password:', error);
- throw error;
- }
+ return bcrypt.compare(candidatePassword, this.password);
};
const User = mongoose.model('User', userSchema);
diff --git a/server/server.js b/server/server.js
index e1e2db4..623db28 100644
--- a/server/server.js
+++ b/server/server.js
@@ -9,15 +9,8 @@ dotenv.config();
// Create Express app
const app = express();
-// Debug middleware
-app.use((req, res, next) => {
- console.log(`${req.method} ${req.url}`);
- // Fix double slashes in URL
- if (req.url.includes('//')) {
- req.url = req.url.replace(/\/+/g, '/');
- }
- next();
-});
+// Middleware
+app.use(express.json());
// CORS configuration
const corsOptions = {
@@ -41,23 +34,18 @@ app.use(cors(corsOptions));
// Handle preflight requests
app.options('*', cors(corsOptions));
-// Middleware
-app.use(express.json());
-
// Database connection
mongoose.connect(process.env.MONGODB_URI)
.then(() => console.log('Connected to MongoDB'))
.catch((err) => console.error('MongoDB connection error:', err));
// Routes
-console.log('Setting up routes...');
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/projects', require('./routes/projects'));
// 404 handler
-app.use((req, res, next) => {
- console.log('404 Not Found:', req.method, req.url);
+app.use((req, res) => {
res.status(404).json({ message: 'Route not found' });
});
@@ -74,8 +62,4 @@ app.use((err, req, res, next) => {
const PORT = process.env.PORT || 5001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
- console.log('Available routes:');
- console.log('- POST /api/auth/login');
- console.log('- POST /api/auth/signup');
- console.log('- GET /api/auth/me');
});
\ No newline at end of file
From b77013eaa944b08bd789eadf6edd48353c2457da Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 2 Apr 2026 04:15:45 +0000
Subject: [PATCH 8/8] perf: database indexes, eliminate double-query delete,
remove hot-path debug logging
Agent-Logs-Url: https://github.com/Karanpratap7/DevLinkUp/sessions/dc173194-4cc5-424f-8c61-aec8a0b36824
Co-authored-by: Karanpratap7 <137878890+Karanpratap7@users.noreply.github.com>
---
client/src/pages/ProjectDetail.jsx | 4 ++-
client/src/services/api.js | 37 +++----------------------
server/controllers/projectController.js | 2 +-
server/models/Project.js | 5 ++++
server/models/User.js | 3 ++
server/routes/projects.js | 11 +++++++-
server/tests/projects.test.js | 2 +-
7 files changed, 27 insertions(+), 37 deletions(-)
diff --git a/client/src/pages/ProjectDetail.jsx b/client/src/pages/ProjectDetail.jsx
index 03acfeb..e42a826 100644
--- a/client/src/pages/ProjectDetail.jsx
+++ b/client/src/pages/ProjectDetail.jsx
@@ -117,7 +117,9 @@ export default function ProjectDetail() {
}
// project.owner may be a populated object or a plain string/ObjectId depending on context
- const ownerId = project.owner?._id ?? project.owner;
+ const ownerId = typeof project.owner === 'object' && project.owner !== null
+ ? project.owner._id
+ : project.owner;
const isOwner = currentUser && ownerId?.toString() === currentUser._id?.toString();
return (
diff --git a/client/src/services/api.js b/client/src/services/api.js
index b976dcb..bf9eaef 100644
--- a/client/src/services/api.js
+++ b/client/src/services/api.js
@@ -24,36 +24,15 @@ api.interceptors.request.use(
if (config.url) {
config.url = config.url.replace(/^\/+/, '');
}
- // Debug log the request
- console.log('Making request:', {
- method: config.method,
- url: `${config.baseURL}/${config.url}`,
- headers: config.headers,
- data: config.data
- });
return config;
},
- (error) => {
- console.error('Request error:', error);
- return Promise.reject(error);
- }
+ (error) => Promise.reject(error)
);
// Add response interceptor to handle errors
api.interceptors.response.use(
- (response) => {
- console.log('Response received:', {
- status: response.status,
- data: response.data
- });
- return response;
- },
+ (response) => response,
(error) => {
- console.error('Response error:', {
- status: error.response?.status,
- data: error.response?.data,
- message: error.message
- });
if (error.response?.status === 401) {
// Handle unauthorized access
localStorage.removeItem('token');
@@ -65,23 +44,15 @@ api.interceptors.response.use(
// Auth API calls
export const authAPI = {
- login: (email, password) => {
- console.log('Login attempt:', { email });
- return api.post('api/auth/login', { email, password });
- },
- register: (name, email, password) => {
- console.log('Register attempt:', { name, email });
- return api.post('api/auth/signup', { name, email, password });
- },
+ login: (email, password) => api.post('api/auth/login', { email, password }),
+ register: (name, email, password) => api.post('api/auth/signup', { name, email, password }),
getCurrentUser: () => api.get('api/auth/me'),
};
// User API calls
export const userAPI = {
getUser: (userId) => api.get(`api/users/${userId}`),
- getProfile: (userId) => api.get(`api/users/${userId}`),
updateProfile: (data) => api.put('api/users/profile', data),
- updateUser: (userId, data) => api.put(`api/users/${userId}`, data),
getAllUsers: () => api.get('api/users'),
};
diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js
index 1ac21b9..fe54a5e 100644
--- a/server/controllers/projectController.js
+++ b/server/controllers/projectController.js
@@ -121,7 +121,7 @@ exports.deleteProject = async (req, res) => {
return res.status(401).json({ message: 'Not authorized' });
}
- await Project.findByIdAndDelete(req.params.id);
+ await project.deleteOne();
res.status(200).json({ message: 'Project deleted' });
} catch (error) {
console.error('Delete project error:', error);
diff --git a/server/models/Project.js b/server/models/Project.js
index 05f33fb..5dcbbeb 100644
--- a/server/models/Project.js
+++ b/server/models/Project.js
@@ -48,6 +48,11 @@ projectSchema.pre('save', function(next) {
next();
});
+// Compound index covers getUserProjects (filter by owner + sort by createdAt)
+projectSchema.index({ owner: 1, createdAt: -1 });
+// Covers searchProjectsByTech ($in on techStack)
+projectSchema.index({ techStack: 1 });
+
const Project = mongoose.model('Project', projectSchema);
module.exports = Project;
\ No newline at end of file
diff --git a/server/models/User.js b/server/models/User.js
index 8056f06..3aaa144 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -64,6 +64,9 @@ userSchema.methods.comparePassword = async function(candidatePassword) {
return bcrypt.compare(candidatePassword, this.password);
};
+// Index for frequently-queried field
+userSchema.index({ skills: 1 });
+
const User = mongoose.model('User', userSchema);
module.exports = User;
\ No newline at end of file
diff --git a/server/routes/projects.js b/server/routes/projects.js
index 95a5b6c..ca06b99 100644
--- a/server/routes/projects.js
+++ b/server/routes/projects.js
@@ -1,9 +1,18 @@
const express = require('express');
const { check } = require('express-validator');
+const rateLimit = require('express-rate-limit');
const router = express.Router();
const projectController = require('../controllers/projectController');
const auth = require('../middleware/auth');
+const mutationLimiter = rateLimit({
+ windowMs: 60 * 1000,
+ max: 30,
+ standardHeaders: true,
+ legacyHeaders: false,
+ message: { message: 'Too many requests, please try again later.' },
+});
+
// @route GET api/projects
// @desc Get all projects
// @access Public
@@ -59,6 +68,6 @@ router.put(
// @route DELETE api/projects/:id
// @desc Delete project
// @access Private
-router.delete('/:id', auth, projectController.deleteProject);
+router.delete('/:id', mutationLimiter, auth, projectController.deleteProject);
module.exports = router;
\ No newline at end of file
diff --git a/server/tests/projects.test.js b/server/tests/projects.test.js
index adaebad..b445b37 100644
--- a/server/tests/projects.test.js
+++ b/server/tests/projects.test.js
@@ -25,6 +25,7 @@ const mockProjectData = {
githubUrl: 'https://github.com/test/project',
demoUrl: 'https://demo.example.com',
owner: { toString: () => 'user123' },
+ deleteOne: jest.fn().mockResolvedValue({}),
};
jest.mock('../models/User', () => {
@@ -322,7 +323,6 @@ describe('Project Routes', () => {
describe('DELETE /api/projects/:id', () => {
it('deletes project the user owns', async () => {
Project.findById.mockResolvedValue(mockProjectData);
- Project.findByIdAndDelete.mockResolvedValue(mockProjectData);
const res = await request(app)
.delete(`/api/projects/${VALID_OID}`)
.set('Authorization', `Bearer ${token}`);