Skip to content

Commit 223df34

Browse files
committed
feat(performance): add benchmarking logs and optimize API key hashing
- Switch API key hashing from scrypt to SHA256 for performance. - Add granular console.time logs in Redis caching, data insertion, and auth middleware. - Refactor CORS check in app.js to use process.hrtime. - Update project controller to clear API key cache on collection creation.
1 parent 9185a34 commit 223df34

7 files changed

Lines changed: 321 additions & 18 deletions

File tree

‎backend/app.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,24 @@ if (process.env.NODE_ENV === 'development') {
3838

3939
const adminCorsOptions = {
4040
origin: function (origin, callback) {
41-
console.time("cors for admin chk")
42-
// Allow requests with no origin (like mobile apps or curl)
43-
if (!origin || adminWhitelist.indexOf(origin) !== -1) {
41+
const start = process.hrtime.bigint();
42+
43+
const allowed = !origin || adminWhitelist.includes(origin);
44+
45+
const end = process.hrtime.bigint();
46+
console.log("Pure CORS check time:",
47+
Number(end - start) / 1e6, "ms");
48+
49+
if (allowed) {
4450
callback(null, true);
4551
} else {
4652
callback(new Error('Not allowed by CORS for Admin access'));
4753
}
48-
console.timeEnd("cors for admin chk")
4954
},
5055
credentials: true
5156
};
5257

58+
5359
if (process.env.NODE_ENV !== 'test') {
5460
GC.garbageCollect();
5561
GC.storageGarbageCollect();

‎backend/controllers/data.controller.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const isValidId = (id) => mongoose.Types.ObjectId.isValid(id);
1010
// INSERT DATA
1111
module.exports.insertData = async (req, res) => {
1212
try {
13+
console.time("insert data")
1314
const { collectionName } = req.params;
1415
const project = req.project;
1516

@@ -61,6 +62,7 @@ module.exports.insertData = async (req, res) => {
6162
);
6263
}
6364

65+
console.timeEnd("insert data")
6466
res.status(201).json(result);
6567
} catch (err) {
6668
res.status(500).json({ error: err.message });

‎backend/controllers/project.controller.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ module.exports.createCollection = async (req, res) => {
262262

263263
await deleteProjectById(projectId);
264264
await setProjectById(projectId, project);
265+
await deleteProjectByApiKeyCache(project.apiKey);
266+
await deleteProjectByApiKeyCache(project.apiKey);
265267
// Safe Response
266268
const projectObj = project.toObject();
267269
delete projectObj.apiKey;
@@ -345,6 +347,7 @@ module.exports.getData = async (req, res) => {
345347

346348
module.exports.insertData = async (req, res) => {
347349
try {
350+
console.time("insert data")
348351
const { projectId, collectionName } = req.params;
349352
const project = await Project.findOne({ _id: projectId, owner: req.user._id });
350353
if (!project) return res.status(404).json({ error: "Project not found." });
@@ -377,6 +380,7 @@ module.exports.insertData = async (req, res) => {
377380
project.databaseUsed = (project.databaseUsed || 0) + docSize;
378381
}
379382
await project.save();
383+
console.timeEnd("insert data")
380384

381385
res.json(result);
382386
} catch (err) {

‎backend/middleware/verifyApiKey.js‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const {
77

88
module.exports = async (req, res, next) => {
99
try {
10-
console.time("api chk middleware")
1110
const apiKey = req.header('x-api-key');
1211
if (!apiKey) {
1312
return res.status(401).json({ error: 'API key not found' });
@@ -45,21 +44,26 @@ module.exports = async (req, res, next) => {
4544
await setProjectByApiKeyCache(hashedApi, project);
4645
}
4746

47+
console.time("checking if owner is verified")
4848
if (!project.owner.isVerified) {
4949
return res.status(401).json({
5050
error: 'Owner not verified',
5151
fix: 'Verify your account on https://urbackend.bitbros.in/dashboard'
5252
});
5353
}
54+
console.timeEnd("checking if owner is verified")
5455

5556
// Ensure defaults are present (crucial for lean objects or cached POJOs)
57+
console.time("setting defaults")
5658
if (!project.resources) project.resources = {};
5759
if (!project.resources.db) project.resources.db = { isExternal: false };
5860
if (!project.resources.storage) project.resources.storage = { isExternal: false };
61+
console.timeEnd("setting defaults")
5962

63+
console.time("setting project and hashed api key")
6064
req.project = project;
6165
req.hashedApiKey = hashedApi;
62-
console.timeEnd("api chk middleware")
66+
console.timeEnd("setting project and hashed api key")
6367
next();
6468
} catch (err) {
6569
res.status(500).json({ error: err.message });

‎backend/services/redisCaching.js‎

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,42 @@ const redis = require("../config/redis");
22

33
async function setProjectByApiKeyCache(api, project) {
44
if (redis.status !== "ready") return;
5+
56
try {
7+
console.time("cache stringify");
68
const data = JSON.stringify(project);
9+
console.timeEnd("cache stringify");
10+
11+
console.time("redis set");
712
await redis.set(
813
`project:apikey:${api}`,
914
data,
1015
'EX',
1116
60 * 60 * 2
1217
);
18+
console.timeEnd("redis set");
19+
1320
} catch (err) {
1421
console.log(err);
1522
}
1623
}
1724

1825
async function getProjectByApiKeyCache(api) {
1926
if (redis.status !== "ready") return null;
27+
2028
try {
29+
console.time("redis get");
2130
const data = await redis.get(`project:apikey:${api}`);
31+
console.timeEnd("redis get");
32+
2233
if (!data) return null;
2334

24-
const parsedData = JSON.parse(data)
35+
console.time("cache parse");
36+
const parsedData = JSON.parse(data);
37+
console.timeEnd("cache parse");
38+
2539
return parsedData;
40+
2641
} catch (err) {
2742
console.log(err);
2843
return null;
@@ -38,37 +53,50 @@ async function deleteProjectByApiKeyCache(api) {
3853
}
3954
}
4055

41-
4256
async function setProjectById(id, project) {
4357
if (redis.status !== "ready") return;
58+
4459
try {
60+
console.time("cache stringify by id");
4561
const data = JSON.stringify(project);
62+
console.timeEnd("cache stringify by id");
63+
64+
console.time("redis set by id");
4665
await redis.set(
4766
`project:id:${id}`,
4867
data,
4968
'EX',
5069
60 * 60 * 2
5170
);
71+
console.timeEnd("redis set by id");
72+
5273
} catch (err) {
5374
console.log(err);
5475
}
5576
}
5677

57-
5878
async function getProjectById(id) {
5979
if (redis.status !== "ready") return null;
80+
6081
try {
82+
console.time("redis get by id");
6183
const data = await redis.get(`project:id:${id}`);
84+
console.timeEnd("redis get by id");
85+
6286
if (!data) return null;
63-
const parsedData = JSON.parse(data)
87+
88+
console.time("cache parse by id");
89+
const parsedData = JSON.parse(data);
90+
console.timeEnd("cache parse by id");
91+
6492
return parsedData;
93+
6594
} catch (err) {
6695
console.log(err);
6796
return null;
6897
}
6998
}
7099

71-
72100
async function deleteProjectById(id) {
73101
if (redis.status !== "ready") return;
74102
try {
@@ -78,12 +106,11 @@ async function deleteProjectById(id) {
78106
}
79107
}
80108

81-
82109
module.exports = {
83110
setProjectByApiKeyCache,
84111
getProjectByApiKeyCache,
85112
deleteProjectByApiKeyCache,
86113
setProjectById,
87114
getProjectById,
88115
deleteProjectById
89-
};
116+
};

‎backend/utils/api.js‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@ function generateApiKey() {
1515

1616
//api hashing
1717
function hashApiKey(apikey) {
18-
// scrypt hashing: fast but brute focre resistant
19-
const salt = process.env.API_KEY_SALT;
20-
21-
// derived key of 64 bytes
22-
return crypto.scryptSync(apikey, salt, 64).toString('hex');
18+
return crypto
19+
.createHash("sha256")
20+
.update(apikey)
21+
.digest("hex");
2322
}
2423

2524
module.exports = { generateApiKey, hashApiKey }

0 commit comments

Comments
 (0)