vanta-api 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/api-features.js +41 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanta-api",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Advanced API features and security configuration for Node.js/MongoDB.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"author": "Alireza Aghaee",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"bson": "^
|
|
22
|
+
"bson": "^7.0.0",
|
|
23
23
|
"mongoose": "^7.0.0",
|
|
24
24
|
"pluralize": "^8.0.0",
|
|
25
25
|
"winston": "^3.0.0"
|
package/src/api-features.js
CHANGED
|
@@ -18,8 +18,11 @@ export class ApiFeatures {
|
|
|
18
18
|
constructor(model, query = {}, userRole = "") {
|
|
19
19
|
this.model = model;
|
|
20
20
|
this.query = { ...query };
|
|
21
|
-
if (
|
|
22
|
-
|
|
21
|
+
if (
|
|
22
|
+
!userRole ||
|
|
23
|
+
!Object.keys(securityConfig.accessLevels).includes(userRole)
|
|
24
|
+
) {
|
|
25
|
+
this.userRole = "guest";
|
|
23
26
|
} else {
|
|
24
27
|
this.userRole = userRole;
|
|
25
28
|
}
|
|
@@ -66,46 +69,43 @@ export class ApiFeatures {
|
|
|
66
69
|
return this;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
limitFields(input = "") {
|
|
73
|
+
const rawFields = [input, this.query.fields].filter(Boolean).join(",");
|
|
74
|
+
if (!rawFields) return this;
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const fieldsArray = rawFields
|
|
78
|
-
.split(",")
|
|
79
|
-
.map((f) => f.trim())
|
|
80
|
-
.filter(Boolean);
|
|
81
|
-
|
|
82
|
-
const includeFields = new Set();
|
|
83
|
-
const excludeFields = new Set();
|
|
76
|
+
const validFields = Object.keys(this.model.schema.paths).filter(
|
|
77
|
+
(f) => !securityConfig.forbiddenFields.includes(f)
|
|
78
|
+
);
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
const fieldsArray = rawFields
|
|
81
|
+
.split(",")
|
|
82
|
+
.map((f) => f.trim())
|
|
83
|
+
.filter(Boolean);
|
|
89
84
|
|
|
90
|
-
|
|
85
|
+
const includeFields = new Set();
|
|
86
|
+
const excludeFields = new Set();
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
} else if (excludeFields.size > 0) {
|
|
97
|
-
validFields.forEach((f) => {
|
|
98
|
-
if (!excludeFields.has(f)) project[f] = 1;
|
|
88
|
+
fieldsArray.forEach((f) => {
|
|
89
|
+
if (f.startsWith("-")) excludeFields.add(f.slice(1));
|
|
90
|
+
else includeFields.add(f);
|
|
99
91
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
const project = {};
|
|
93
|
+
if (includeFields.size > 0) {
|
|
94
|
+
includeFields.forEach((f) => {
|
|
95
|
+
if (validFields.includes(f)) project[f] = 1;
|
|
96
|
+
});
|
|
97
|
+
} else if (excludeFields.size > 0) {
|
|
98
|
+
validFields.forEach((f) => {
|
|
99
|
+
if (!excludeFields.has(f)) project[f] = 1;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
105
102
|
|
|
106
|
-
|
|
107
|
-
}
|
|
103
|
+
if (Object.keys(project).length) {
|
|
104
|
+
this.pipeline.push({ $project: project });
|
|
105
|
+
}
|
|
108
106
|
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
109
|
paginate() {
|
|
110
110
|
const { maxLimit } = securityConfig.accessLevels[this.userRole] || {
|
|
111
111
|
maxLimit: 100,
|
|
@@ -252,7 +252,6 @@ export class ApiFeatures {
|
|
|
252
252
|
|
|
253
253
|
_parseQueryFilters() {
|
|
254
254
|
const obj = { ...this.query };
|
|
255
|
-
// پاک کردن پارامترهای سیستماتیک
|
|
256
255
|
["page", "limit", "sort", "fields", "populate"].forEach(
|
|
257
256
|
(k) => delete obj[k]
|
|
258
257
|
);
|
|
@@ -320,9 +319,14 @@ export class ApiFeatures {
|
|
|
320
319
|
}
|
|
321
320
|
|
|
322
321
|
if (typeof val === "string" && /^[0-9]+$/.test(val)) {
|
|
323
|
-
|
|
322
|
+
if (val.length > 1 && val.startsWith("0")) {
|
|
323
|
+
resultObj[keyObj] = val; // keep leading zero
|
|
324
|
+
} else {
|
|
325
|
+
resultObj[keyObj] = parseInt(val, 10);
|
|
326
|
+
}
|
|
324
327
|
return;
|
|
325
328
|
}
|
|
329
|
+
|
|
326
330
|
if (typeof val === "string" && this.#isStrictObjectId(val)) {
|
|
327
331
|
resultObj[keyObj] = new ObjectId(val);
|
|
328
332
|
return;
|
|
@@ -343,9 +347,6 @@ export class ApiFeatures {
|
|
|
343
347
|
_applySecurityFilters(filters) {
|
|
344
348
|
let res = { ...filters };
|
|
345
349
|
securityConfig.forbiddenFields.forEach((f) => delete res[f]);
|
|
346
|
-
if (this.userRole !== "admin" && this.model.schema.path("isActive")) {
|
|
347
|
-
res.isActive = true;
|
|
348
|
-
}
|
|
349
350
|
return res;
|
|
350
351
|
}
|
|
351
352
|
|