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
16 changes: 14 additions & 2 deletions backend/src/controllers/allocation.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,21 @@ export const runAllocation = async (req: Request, res: Response): Promise<void>
const hallDemand = demandResult.rows;
const totalStudents = hallDemand.reduce((sum: number, row: any) => sum + row.count, 0);

// 2. Run solver
// 2. Run solver with error handling
const studentsPerRegion = convertHallDemandToRegions(hallDemand);
const solverResult = solveCabAllocation(studentsPerRegion);
let solverResult;

try {
solverResult = solveCabAllocation(studentsPerRegion);
} catch (error) {
console.error('Solver error:', error);
res.status(400).json({
success: false,
error: 'Unable to create optimal allocation. The booking configuration may not be feasible. Please try manual allocation or contact support.',
details: error instanceof Error ? error.message : 'Solver failed',
});
return;
}

// 3. Get all students with details for random assignment
const studentsResult = await pool.query(
Expand Down
24 changes: 19 additions & 5 deletions backend/src/controllers/qr.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ export const validateQR = async (req: Request, res: Response): Promise<void> =>
// GET CAB DETAILS (for student cab view)
// ====================================
export const getCabDetails = async (req: Request, res: Response): Promise<void> => {
const client = await pool.connect();

try {
const allocationId = req.params.allocationId as string;

Expand All @@ -335,15 +337,19 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
return;
}

// Start transaction for consistent read
await client.query('BEGIN');

// Get cab_id from the allocation
const allocationResult = await pool.query(
const allocationResult = await client.query(
`SELECT cab_id, trip_id, user_id
FROM cab_allocations
WHERE id = $1`,
[allocationId]
);

if (allocationResult.rows.length === 0) {
await client.query('ROLLBACK');
res.status(404).json({
success: false,
error: 'not_found',
Expand All @@ -355,7 +361,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
const { cab_id, trip_id, user_id } = allocationResult.rows[0];

// Check if user has paid for this trip
const paymentCheck = await pool.query(
const paymentCheck = await client.query(
`SELECT p.payment_status
FROM trip_users tu
JOIN payments p ON tu.payment_id = p.id
Expand All @@ -364,6 +370,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
);

if (paymentCheck.rows.length === 0 || paymentCheck.rows[0].payment_status !== 'confirmed') {
await client.query('ROLLBACK');
res.status(403).json({
success: false,
error: 'payment_required',
Expand All @@ -373,7 +380,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
}

// Fetch cab details (WITHOUT passkey - students shouldn't see it)
const cabResult = await pool.query(
const cabResult = await client.query(
`SELECT
c.id,
c.cab_number,
Expand All @@ -388,6 +395,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
);

if (cabResult.rows.length === 0) {
await client.query('ROLLBACK');
res.status(404).json({
success: false,
error: 'not_found',
Expand All @@ -399,7 +407,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
const cab = cabResult.rows[0];

// Fetch all students in this cab
const studentsResult = await pool.query(
const studentsResult = await client.query(
`SELECT
ca.user_id,
ca.id as booking_id,
Expand Down Expand Up @@ -431,7 +439,7 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>

// Fetch ALL OTHER cabs for this trip (for return journey options)
// Only return driver details and starting point - no passenger info
const otherCabsResult = await pool.query(
const otherCabsResult = await client.query(
`SELECT
c.id,
c.cab_number,
Expand All @@ -447,6 +455,9 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>

const otherCabs = otherCabsResult.rows;

// Commit transaction
await client.query('COMMIT');

res.status(200).json({
success: true,
data: {
Expand All @@ -456,13 +467,16 @@ export const getCabDetails = async (req: Request, res: Response): Promise<void>
},
});
} catch (error: any) {
await client.query('ROLLBACK');
console.error('Error fetching cab details:', error);
res.status(500).json({
success: false,
error: 'server_error',
message: 'Failed to fetch cab details',
details: error.message,
});
} finally {
client.release();
}
};

25 changes: 20 additions & 5 deletions backend/src/middleware/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ export const authenticateUser = async (
const token = req.headers.authorization?.replace('Bearer ', '');

if (!token) {
return res.status(401).json({ error: 'No token provided' });
return res.status(401).json({
success: false,
error: 'No token provided'
});
}

// Verify JWT token
const decoded = verifyToken(token);

if (!decoded) {
return res.status(401).json({ error: 'Invalid or expired token' });
return res.status(401).json({
success: false,
error: 'Invalid or expired token'
});
}

// Fetch user details from database to ensure user still exists
Expand All @@ -31,7 +37,10 @@ export const authenticateUser = async (
);

if (result.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
return res.status(404).json({
success: false,
error: 'User not found'
});
}

const userData = result.rows[0];
Expand All @@ -48,7 +57,10 @@ export const authenticateUser = async (
next();
} catch (error) {
console.error('Authentication error:', error);
res.status(500).json({ error: 'Authentication failed' });
res.status(500).json({
success: false,
error: 'Authentication failed'
});
}
};

Expand All @@ -58,7 +70,10 @@ export const requireAdmin = (
next: NextFunction
) => {
if (!req.user?.isAdmin) {
return res.status(403).json({ error: 'Admin access required' });
return res.status(403).json({
success: false,
error: 'Admin access required'
});
}
next();
};
10 changes: 8 additions & 2 deletions backend/src/middleware/cron.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ export const verifyCronSecret = (req: Request, res: Response, next: NextFunction

if (!cronSecret) {
console.error('❌ CRON_SECRET environment variable not set');
return res.status(500).json({ error: 'Server misconfiguration' });
return res.status(500).json({
success: false,
error: 'Server misconfiguration'
});
}

if (secret !== cronSecret) {
console.warn('⚠️ Invalid cron secret attempt');
return res.status(401).json({ error: 'Unauthorized' });
return res.status(401).json({
success: false,
error: 'Unauthorized'
});
}

next();
Expand Down
10 changes: 8 additions & 2 deletions backend/src/middleware/webhook.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export const requireWebhookSignature = (
const signature = req.headers['x-razorpay-signature'];

if (!signature) {
res.status(401).json({ error: 'Missing webhook signature' });
res.status(401).json({
success: false,
error: 'Missing webhook signature'
});
return;
}

Expand Down Expand Up @@ -85,7 +88,10 @@ export const webhookRateLimiter = (

if (record.count >= maxRequests) {
console.warn(`Webhook rate limit exceeded for IP: ${ip}`);
res.status(429).json({ error: 'Too many requests' });
res.status(429).json({
success: false,
error: 'Too many requests'
});
return;
}

Expand Down
43 changes: 34 additions & 9 deletions backend/src/routes/auth.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,19 @@ router.get('/me', authenticateUser, async (req, res) => {
);

if (result.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
return res.status(404).json({
success: false,
error: 'User not found'
});
}

res.json(formatUserResponse(result.rows[0]));
} catch (error) {
console.error('Error fetching user profile:', error);
res.status(500).json({ error: 'Failed to fetch user' });
res.status(500).json({
success: false,
error: 'Failed to fetch user'
});
}
});

Expand Down Expand Up @@ -143,7 +149,10 @@ router.put('/me', authenticateUser, validateProfileUpdate, async (req, res) => {
});
} catch (error) {
console.error('Error updating user profile:', error);
res.status(500).json({ error: 'Failed to update user profile' });
res.status(500).json({
success: false,
error: 'Failed to update user profile'
});
}
});

Expand All @@ -161,14 +170,20 @@ router.post('/verify-login', async (req, res) => {
try {
// Check if verification login is enabled
if (process.env.ENABLE_VERIFY_LOGIN !== 'true') {
return res.status(404).json({ error: 'Not found' });
return res.status(404).json({
success: false,
error: 'Not found'
});
}

const { email, password } = req.body;

// Validate input
if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
return res.status(400).json({
success: false,
error: 'Email and password are required'
});
}

// Check against environment variables
Expand All @@ -177,12 +192,18 @@ router.post('/verify-login', async (req, res) => {

if (!verifyEmail || !verifyPassword) {
console.error('VERIFY_EMAIL or VERIFY_PASSWORD not configured');
return res.status(500).json({ error: 'Verification login not configured' });
return res.status(500).json({
success: false,
error: 'Verification login not configured'
});
}

// Validate credentials
// Verify credentials
if (email !== verifyEmail || password !== verifyPassword) {
return res.status(401).json({ error: 'Invalid email or password' });
return res.status(401).json({
success: false,
error: 'Invalid email or password'
});
}

// Find user in database
Expand All @@ -193,6 +214,7 @@ router.post('/verify-login', async (req, res) => {

if (result.rows.length === 0) {
return res.status(401).json({
success: false,
error: 'User not found. Please ensure the test user exists in the database.'
});
}
Expand All @@ -214,7 +236,10 @@ router.post('/verify-login', async (req, res) => {
});
} catch (error) {
console.error('Verify login error:', error);
res.status(500).json({ error: 'Login failed' });
res.status(500).json({
success: false,
error: 'Login failed'
});
}
});

Expand Down
10 changes: 8 additions & 2 deletions backend/src/routes/webhook.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ router.post('/razorpay', async (req: Request, res: Response) => {

if (!signature) {
console.error('Webhook missing signature header');
res.status(400).json({ error: 'Missing signature header' });
res.status(400).json({
success: false,
error: 'Missing signature header'
});
return;
}

Expand All @@ -51,7 +54,10 @@ router.post('/razorpay', async (req: Request, res: Response) => {

if (!rawBody) {
console.error('Webhook missing raw body');
res.status(400).json({ error: 'Missing request body' });
res.status(400).json({
success: false,
error: 'Missing request body'
});
return;
}

Expand Down
Loading