Skip to content

Commit 0f0f4a8

Browse files
committed
Fix: Security vulnerabilities and added fault tolerance
1 parent 561b3f0 commit 0f0f4a8

5 files changed

Lines changed: 139 additions & 32 deletions

File tree

backend/app.js

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,26 @@ app.use(express.urlencoded({ extended: true }));
1111

1212

1313
console.log(process.env.MONGO_URL);
14-
mongoose.connect(process.env.MONGO_URL)
15-
.then((d) => {
16-
console.log("connected");
17-
})
18-
.catch((e) => {
19-
console.log('not connected')
20-
})
14+
const connectDB = async () => {
15+
try {
16+
await mongoose.connect(process.env.MONGO_URL);
17+
console.log("✅ MongoDB Connected");
18+
} catch (err) {
19+
console.error("❌ MongoDB Connection Error:", err);
20+
setTimeout(connectDB, 5000);
21+
}
22+
};
23+
24+
mongoose.connection.on('error', (err) => {
25+
console.error("MongoDB Runtime Error:", err);
26+
});
27+
28+
mongoose.connection.on('disconnected', () => {
29+
console.warn("MongoDB Disconnected. Retrying...");
30+
connectDB();
31+
});
32+
33+
connectDB();
2134

2235

2336
app.get('/', (req, res) => {
@@ -40,13 +53,48 @@ app.use('/api/', limiter);
4053
app.use('/api/auth', authRoute);
4154
app.use('/api/projects', projectRoute);
4255

43-
// Logger added to userAuth route 👇
4456
app.use('/api/userAuth', logger, userAuthRoute);
4557

4658
app.use('/api/data', verifyApiKey, logger, dataRoute);
4759
app.use('/api/storage', verifyApiKey, logger, storageRoute);
4860

61+
app.use((err, req, res, next) => {
62+
console.error("Unhandled Error:", err);
63+
res.status(500).json({ error: "Something went wrong!" });
64+
});
65+
4966
const PORT = process.env.PORT || 1234;
50-
app.listen(PORT, () => {
67+
68+
69+
const server = app.listen(PORT, () => {
5170
console.log(`Server running on port ${PORT}`);
52-
});
71+
});
72+
73+
// 🛡️ GRACEFUL SHUTDOWN (Fixed for Mongoose 8+)
74+
const gracefulShutdown = async () => {
75+
console.log('🛑 SIGTERM/SIGINT received. Shutting down gracefully...');
76+
77+
server.close(async () => {
78+
console.log('✅ HTTP server closed.');
79+
80+
try {
81+
// Updated: No callback, use await instead
82+
await mongoose.connection.close(false);
83+
console.log('✅ MongoDB connection closed.');
84+
process.exit(0);
85+
} catch (err) {
86+
console.error('❌ Error closing MongoDB connection:', err);
87+
process.exit(1);
88+
}
89+
});
90+
91+
// Force close after 10 seconds
92+
setTimeout(() => {
93+
console.error('Force shutting down...');
94+
process.exit(1);
95+
}, 10000);
96+
};
97+
98+
// Listen for termination signals (e.g., from PM2, Docker, or Ctrl+C)
99+
process.on('SIGTERM', gracefulShutdown);
100+
process.on('SIGINT', gracefulShutdown);

backend/package-lock.json

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"jsonwebtoken": "^9.0.2",
2020
"mongoose": "^8.19.2",
2121
"multer": "^2.0.2",
22-
"uuid": "^13.0.0"
22+
"uuid": "^13.0.0",
23+
"zod": "^4.1.13"
2324
}
2425
}

backend/routes/auth.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ const express = require('express');
22
const router = express.Router();
33
const bcrypt = require('bcryptjs');
44
const jwt = require('jsonwebtoken');
5+
const { z } = require('zod');
56
const authorization = require('../middleware/authMiddleware'); // Middleware
67
const Developer = require('../models/Developer');
7-
const Project = require('../models/Project'); // Project model bhi chahiye delete ke liye
8+
const Project = require('../models/Project');
9+
const loginSchema = z.object({
10+
email: z.string().email(),
11+
password: z.string().min(6)
12+
});
813

914
// 1. REGISTER ROUTE
1015
router.post('/register', async (req, res) => {
1116
try {
12-
const { email, password } = req.body;
17+
18+
const { email, password } = loginSchema.parse(req.body);
19+
1320
const existingUser = await Developer.findOne({ email });
1421
if (existingUser) return res.status(400).send("Email already exists");
1522

@@ -20,14 +27,20 @@ router.post('/register', async (req, res) => {
2027
await newDev.save();
2128
res.status(201).send("Registered successfully");
2229
} catch (err) {
23-
res.status(500).send(err.message);
30+
31+
if (err instanceof z.ZodError) {
32+
return res.status(400).json({ error: err.errors });
33+
}
34+
35+
console.log(err);
36+
res.status(500).send('Some issue at our end');
2437
}
2538
});
2639

2740
// 2. LOGIN ROUTE
2841
router.post('/login', async (req, res) => {
2942
try {
30-
const { email, password } = req.body;
43+
const { email, password } = loginSchema.parse(req.body);
3144
const dev = await Developer.findOne({ email });
3245
if (!dev) return res.status(400).send("User not found");
3346

@@ -37,7 +50,11 @@ router.post('/login', async (req, res) => {
3750
const token = jwt.sign({ _id: dev._id }, process.env.JWT_SECRET);
3851
res.json({ token });
3952
} catch (err) {
40-
res.status(500).send(err.message);
53+
if (err instanceof z.ZodError) {
54+
return res.status(400).json({ error: err.errors });
55+
}
56+
console.log(err);
57+
res.status(500).send('Some issue at our end');
4158
}
4259
});
4360

@@ -60,7 +77,11 @@ router.put('/change-password', authorization, async (req, res) => {
6077

6178
res.send("Password updated successfully");
6279
} catch (err) {
63-
res.status(500).send(err.message);
80+
if (err instanceof z.ZodError) {
81+
return res.status(400).json({ error: err.errors });
82+
}
83+
console.log(err);
84+
res.status(500).send('Some issue at our end');
6485
}
6586
});
6687

@@ -81,7 +102,11 @@ router.delete('/delete-account', authorization, async (req, res) => {
81102

82103
res.send("Account and all projects deleted.");
83104
} catch (err) {
84-
res.status(500).send(err.message);
105+
if (err instanceof z.ZodError) {
106+
return res.status(400).json({ error: err.errors });
107+
}
108+
console.log(err);
109+
res.status(500).send('Some issue at our end');
85110
}
86111
});
87112

backend/routes/data.js

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ router.post('/:collectionName', verifyApiKey, async (req, res) => {
1515
const currentProject = await Project.findById(project._id);
1616

1717
const collectionConfig = currentProject.collections.find(c => c.name === collectionName);
18-
if (!collectionConfig) return res.status(404).send(`Collection '${collectionName}' not found.`);
18+
if (!collectionConfig) {
19+
return res.status(404).json({
20+
error: "Collection not found",
21+
collection: collectionName
22+
});
23+
}
1924

2025
const schemaRules = collectionConfig.model;
2126
const incomingData = req.body;
@@ -24,7 +29,10 @@ router.post('/:collectionName', verifyApiKey, async (req, res) => {
2429
// --- VALIDATION & SANITIZATION ---
2530
for (const field of schemaRules) {
2631
if (field.required && incomingData[field.key] === undefined) {
27-
return res.status(400).send(`Field '${field.key}' is required.`);
32+
return res.status(400).json({
33+
error: `Field '${field.key}' is required.`,
34+
field: field.key
35+
});
2836
}
2937
if (incomingData[field.key] !== undefined) {
3038
// Type checking logic (same as before)...
@@ -59,7 +67,8 @@ router.post('/:collectionName', verifyApiKey, async (req, res) => {
5967
});
6068

6169
} catch (err) {
62-
res.status(500).send(err.message);
70+
console.log(err)
71+
res.status(500).send('Some issue at our end');
6372
}
6473
});
6574

@@ -73,7 +82,10 @@ router.get('/:collectionName', verifyApiKey, async (req, res) => {
7382

7483
const collectionConfig = project.collections.find(c => c.name === collectionName);
7584
if (!collectionConfig) {
76-
return res.status(404).send(`Collection '${collectionName}' not found.`);
85+
return res.status(404).json({
86+
error: "Collection not found",
87+
collection: collectionName
88+
});
7789
}
7890
const finalCollectionName = `${project._id}_${collectionName}`;
7991

@@ -85,7 +97,8 @@ router.get('/:collectionName', verifyApiKey, async (req, res) => {
8597
res.json(data);
8698

8799
} catch (err) {
88-
res.status(500).send(err.message);
100+
console.log(err)
101+
res.status(500).send('Some issue at our end');
89102
}
90103
});
91104

@@ -99,7 +112,12 @@ router.get('/:collectionName/:id', verifyApiKey, async (req, res) => {
99112

100113
// Security Check
101114
const collectionConfig = project.collections.find(c => c.name === collectionName);
102-
if (!collectionConfig) return res.status(404).send(`Collection not found.`);
115+
if (!collectionConfig) {
116+
return res.status(404).json({
117+
error: "Collection not found",
118+
collection: collectionName
119+
});
120+
}
103121

104122
const finalCollectionName = `${project._id}_${collectionName}`;
105123

@@ -115,8 +133,8 @@ router.get('/:collectionName/:id', verifyApiKey, async (req, res) => {
115133
res.json(doc);
116134

117135
} catch (err) {
118-
// Agar ID format galat hai toh MongoDB error dega
119-
res.status(400).send("Invalid ID format or Server Error: " + err.message);
136+
console.log(err)
137+
res.status(500).send('Some issue at our end');
120138
}
121139
});
122140

@@ -153,7 +171,8 @@ router.delete('/:collectionName/:id', verifyApiKey, async (req, res) => {
153171
res.json({ message: "Document deleted", id });
154172

155173
} catch (err) {
156-
res.status(400).send("Error: " + err.message);
174+
console.log(err)
175+
res.status(500).send('Some issue at our end');
157176
}
158177
});
159178

@@ -169,11 +188,14 @@ router.put('/:collectionName/:id', verifyApiKey, async (req, res) => {
169188

170189
// Security & Config Find
171190
const collectionConfig = project.collections.find(c => c.name === collectionName);
172-
if (!collectionConfig) return res.status(404).send(`Collection not found.`);
173-
191+
if (!collectionConfig) {
192+
return res.status(404).json({
193+
error: "Collection not found",
194+
collection: collectionName
195+
});
196+
}
174197
// --- VALIDATION REPEAT (Zaroori hai) ---
175198
const schemaRules = collectionConfig.model;
176-
// Hum loop chala kar check karenge ki jo fields update ho rahe hain woh sahi hain ya nahi
177199
for (const key in incomingData) {
178200
// 1. Check if field exists in schema
179201
const fieldRule = schemaRules.find(f => f.key === key);
@@ -210,7 +232,8 @@ router.put('/:collectionName/:id', verifyApiKey, async (req, res) => {
210232
res.json({ message: "Document updated successfully", id, updatedFields: incomingData });
211233

212234
} catch (err) {
213-
res.status(400).send("Invalid ID format, Validation Error or Server Error: " + err.message);
235+
console.log(err)
236+
res.status(500).send('Some issue at our end');
214237
}
215238
});
216239

0 commit comments

Comments
 (0)