elseware-nodejs 1.8.7 → 1.9.0
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/dist/index.cjs +541 -299
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -102
- package/dist/index.d.ts +227 -102
- package/dist/index.js +524 -292
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import mongoose from 'mongoose';
|
|
2
1
|
import dotenv from 'dotenv';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
3
|
import { BlobServiceClient, StorageSharedKeyCredential, generateBlobSASQueryParameters, BlobSASPermissions } from '@azure/storage-blob';
|
|
4
4
|
import fs3 from 'fs';
|
|
5
5
|
import cloudinaryModule from 'cloudinary';
|
|
@@ -10,252 +10,7 @@ import juice from 'juice';
|
|
|
10
10
|
import jwt from 'jsonwebtoken';
|
|
11
11
|
import multer from 'multer';
|
|
12
12
|
|
|
13
|
-
// src/
|
|
14
|
-
var AppError = class extends Error {
|
|
15
|
-
statusCode;
|
|
16
|
-
status;
|
|
17
|
-
isOperational;
|
|
18
|
-
code;
|
|
19
|
-
details;
|
|
20
|
-
constructor(message, statusCode = 500, options) {
|
|
21
|
-
super(message);
|
|
22
|
-
this.statusCode = statusCode;
|
|
23
|
-
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
|
|
24
|
-
this.isOperational = true;
|
|
25
|
-
this.code = options?.code;
|
|
26
|
-
this.details = options?.details;
|
|
27
|
-
Error.captureStackTrace(this, this.constructor);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// src/api/apiResponse.ts
|
|
32
|
-
var APIResponse = class _APIResponse {
|
|
33
|
-
static send(res, options) {
|
|
34
|
-
const {
|
|
35
|
-
statusCode = 200,
|
|
36
|
-
success = true,
|
|
37
|
-
message = "Success",
|
|
38
|
-
data,
|
|
39
|
-
meta
|
|
40
|
-
} = options;
|
|
41
|
-
if (statusCode === 204) {
|
|
42
|
-
return res.status(204).send();
|
|
43
|
-
}
|
|
44
|
-
const body = {
|
|
45
|
-
success,
|
|
46
|
-
message,
|
|
47
|
-
...data !== void 0 && { data },
|
|
48
|
-
...meta !== void 0 && { meta }
|
|
49
|
-
};
|
|
50
|
-
return res.status(statusCode).json(body);
|
|
51
|
-
}
|
|
52
|
-
static ok(res, data, message = "Success", meta) {
|
|
53
|
-
return _APIResponse.send(res, { statusCode: 200, data, message, meta });
|
|
54
|
-
}
|
|
55
|
-
static created(res, data, message = "Resource created successfully") {
|
|
56
|
-
return _APIResponse.send(res, { statusCode: 201, data, message });
|
|
57
|
-
}
|
|
58
|
-
static noContent(res) {
|
|
59
|
-
return _APIResponse.send(res, { statusCode: 204 });
|
|
60
|
-
}
|
|
61
|
-
static error(res, message = "Internal Server Error", statusCode = 500, meta) {
|
|
62
|
-
return _APIResponse.send(res, {
|
|
63
|
-
success: false,
|
|
64
|
-
statusCode,
|
|
65
|
-
message,
|
|
66
|
-
meta
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
static paginated(res, data, options, message = "Data fetched successfully") {
|
|
70
|
-
return _APIResponse.ok(res, data, message, {
|
|
71
|
-
total: options.total,
|
|
72
|
-
page: options.page,
|
|
73
|
-
limit: options.limit,
|
|
74
|
-
totalPages: Math.ceil(options.total / options.limit)
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// src/api/asyncHandler.ts
|
|
80
|
-
var asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
|
|
81
|
-
|
|
82
|
-
// src/api/apiFeatures.ts
|
|
83
|
-
var APIFeatures = class {
|
|
84
|
-
query;
|
|
85
|
-
queryString;
|
|
86
|
-
page;
|
|
87
|
-
limit;
|
|
88
|
-
constructor(query, queryString) {
|
|
89
|
-
this.query = query;
|
|
90
|
-
this.queryString = queryString;
|
|
91
|
-
this.page = 1;
|
|
92
|
-
this.limit = 100;
|
|
93
|
-
}
|
|
94
|
-
filter() {
|
|
95
|
-
const queryObj = { ...this.queryString };
|
|
96
|
-
["page", "sort", "limit", "fields"].forEach((el) => delete queryObj[el]);
|
|
97
|
-
let queryStr = JSON.stringify(queryObj);
|
|
98
|
-
queryStr = queryStr.replace(
|
|
99
|
-
/\b(gte|gt|lte|lt|in)\b/g,
|
|
100
|
-
(match) => `$${match}`
|
|
101
|
-
);
|
|
102
|
-
this.query = this.query.find(JSON.parse(queryStr));
|
|
103
|
-
return this;
|
|
104
|
-
}
|
|
105
|
-
sort() {
|
|
106
|
-
if (this.queryString.sort) {
|
|
107
|
-
const sortBy = String(this.queryString.sort).split(",").join(" ");
|
|
108
|
-
this.query = this.query.sort(sortBy);
|
|
109
|
-
} else {
|
|
110
|
-
this.query = this.query.sort("-createdAt");
|
|
111
|
-
}
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
limitFields() {
|
|
115
|
-
if (this.queryString.fields) {
|
|
116
|
-
const fields = String(this.queryString.fields).split(",").join(" ");
|
|
117
|
-
this.query = this.query.select(fields);
|
|
118
|
-
} else {
|
|
119
|
-
this.query = this.query.select("-__v");
|
|
120
|
-
}
|
|
121
|
-
return this;
|
|
122
|
-
}
|
|
123
|
-
paginate() {
|
|
124
|
-
this.page = Number(this.queryString.page) || 1;
|
|
125
|
-
this.limit = Number(this.queryString.limit) || 100;
|
|
126
|
-
const skip = (this.page - 1) * this.limit;
|
|
127
|
-
this.query = this.query.skip(skip).limit(this.limit);
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
populate(populateOptions) {
|
|
131
|
-
if (populateOptions) {
|
|
132
|
-
this.query = this.query.populate(populateOptions);
|
|
133
|
-
}
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
var apiFeatures_default = APIFeatures;
|
|
138
|
-
|
|
139
|
-
// src/api/apiFactory.ts
|
|
140
|
-
var APIFactory = class {
|
|
141
|
-
static getAll(Model, options = {}) {
|
|
142
|
-
return asyncHandler(async (req, res) => {
|
|
143
|
-
const filter = options.filter ? options.filter(req) : {};
|
|
144
|
-
const features = new apiFeatures_default(Model.find(filter), req.query).filter().sort().limitFields().paginate().populate(options.populate);
|
|
145
|
-
const docs = await features.query;
|
|
146
|
-
APIResponse.ok(
|
|
147
|
-
res,
|
|
148
|
-
docs,
|
|
149
|
-
options.message ?? "Records fetched successfully",
|
|
150
|
-
{
|
|
151
|
-
count: docs.length,
|
|
152
|
-
page: features.page,
|
|
153
|
-
limit: features.limit
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
static getOne(Model, options = {}) {
|
|
159
|
-
return asyncHandler(async (req, res) => {
|
|
160
|
-
let query = Model.findById(req.params.id);
|
|
161
|
-
if (options.populate) {
|
|
162
|
-
query = query.populate(
|
|
163
|
-
options.populate
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
const doc = await query;
|
|
167
|
-
if (!doc) {
|
|
168
|
-
throw new AppError(
|
|
169
|
-
options.notFoundMessage ?? "Resource not found",
|
|
170
|
-
404,
|
|
171
|
-
{ code: "RESOURCE_NOT_FOUND" }
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
APIResponse.ok(res, doc);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
static createOne(Model, options = {}) {
|
|
178
|
-
return asyncHandler(async (req, res) => {
|
|
179
|
-
const doc = await Model.create(req.body);
|
|
180
|
-
APIResponse.created(
|
|
181
|
-
res,
|
|
182
|
-
doc,
|
|
183
|
-
options.message ?? "Resource created successfully"
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
static updateOne(Model, options = {}) {
|
|
188
|
-
return asyncHandler(async (req, res) => {
|
|
189
|
-
const doc = await Model.findByIdAndUpdate(
|
|
190
|
-
req.params.id,
|
|
191
|
-
req.body,
|
|
192
|
-
{
|
|
193
|
-
new: true,
|
|
194
|
-
runValidators: true
|
|
195
|
-
}
|
|
196
|
-
);
|
|
197
|
-
if (!doc) {
|
|
198
|
-
throw new AppError(
|
|
199
|
-
options.notFoundMessage ?? "Resource not found",
|
|
200
|
-
404
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
APIResponse.ok(
|
|
204
|
-
res,
|
|
205
|
-
doc,
|
|
206
|
-
options.message ?? "Resource updated successfully"
|
|
207
|
-
);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
static deleteOne(Model, options = {}) {
|
|
211
|
-
return asyncHandler(async (req, res) => {
|
|
212
|
-
const doc = await Model.findByIdAndDelete(req.params.id);
|
|
213
|
-
if (!doc) {
|
|
214
|
-
throw new AppError(
|
|
215
|
-
options.notFoundMessage ?? "Resource not found",
|
|
216
|
-
404
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
APIResponse.noContent(res);
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// src/api/pickFields.ts
|
|
225
|
-
function pickFields(obj, fields, options = {}) {
|
|
226
|
-
const removeUndefined = options.removeUndefined ?? false;
|
|
227
|
-
const removeNull = options.removeNull ?? false;
|
|
228
|
-
const removeEmptyString = options.removeEmptyString ?? false;
|
|
229
|
-
const strict = options.strict ?? false;
|
|
230
|
-
const defaults = options.defaults ?? {};
|
|
231
|
-
const rename = options.rename ?? {};
|
|
232
|
-
const transform = options.transform ?? {};
|
|
233
|
-
const result = {};
|
|
234
|
-
if (strict) {
|
|
235
|
-
for (const key of Object.keys(obj)) {
|
|
236
|
-
if (!fields.includes(key)) {
|
|
237
|
-
throw new Error(`Unknown field: ${key}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
for (const field of fields) {
|
|
242
|
-
let value = obj[field];
|
|
243
|
-
if (value === void 0 && field in defaults) {
|
|
244
|
-
value = defaults[field];
|
|
245
|
-
}
|
|
246
|
-
if (removeUndefined && value === void 0) continue;
|
|
247
|
-
if (removeNull && value === null) continue;
|
|
248
|
-
if (removeEmptyString && value === "") continue;
|
|
249
|
-
if (transform[field]) {
|
|
250
|
-
value = transform[field](value);
|
|
251
|
-
}
|
|
252
|
-
const outputKey = rename[field] ?? field;
|
|
253
|
-
if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
|
|
254
|
-
result[outputKey] = value;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return result;
|
|
258
|
-
}
|
|
13
|
+
// src/configs/env.ts
|
|
259
14
|
|
|
260
15
|
// src/utils/errorToString.ts
|
|
261
16
|
function errorToString(error) {
|
|
@@ -314,25 +69,7 @@ ${errorToString(error)}`);
|
|
|
314
69
|
};
|
|
315
70
|
var logger = new Logger();
|
|
316
71
|
|
|
317
|
-
// src/configs/
|
|
318
|
-
async function connectMongoDB(config) {
|
|
319
|
-
try {
|
|
320
|
-
mongoose.set("strictQuery", true);
|
|
321
|
-
await mongoose.connect(config.uri);
|
|
322
|
-
logger.success("Database connected successfully");
|
|
323
|
-
process.on("SIGINT", async () => {
|
|
324
|
-
await mongoose.connection.close();
|
|
325
|
-
logger.success("Database connection closed");
|
|
326
|
-
process.exit(0);
|
|
327
|
-
});
|
|
328
|
-
} catch (error) {
|
|
329
|
-
logger.danger("Database connection failed");
|
|
330
|
-
if (error instanceof Error) {
|
|
331
|
-
logger.danger(error.message);
|
|
332
|
-
}
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
72
|
+
// src/configs/env.ts
|
|
336
73
|
dotenv.config({ quiet: true });
|
|
337
74
|
function loadEnv(options) {
|
|
338
75
|
const result = options.schema.safeParse(process.env);
|
|
@@ -2167,6 +1904,7 @@ var CircularQueue = class {
|
|
|
2167
1904
|
}
|
|
2168
1905
|
this.data = new Array(capacity);
|
|
2169
1906
|
}
|
|
1907
|
+
capacity;
|
|
2170
1908
|
data;
|
|
2171
1909
|
head = 0;
|
|
2172
1910
|
tail = 0;
|
|
@@ -5087,22 +4825,403 @@ var SegmentTree = class {
|
|
|
5087
4825
|
}
|
|
5088
4826
|
};
|
|
5089
4827
|
|
|
5090
|
-
// src/
|
|
4828
|
+
// src/database/DatabaseManager.ts
|
|
4829
|
+
var DatabaseManager = class {
|
|
4830
|
+
constructor(provider, options = {
|
|
4831
|
+
enableShutdownHooks: true
|
|
4832
|
+
}) {
|
|
4833
|
+
this.provider = provider;
|
|
4834
|
+
this.options = options;
|
|
4835
|
+
}
|
|
4836
|
+
provider;
|
|
4837
|
+
options;
|
|
4838
|
+
async connect() {
|
|
4839
|
+
try {
|
|
4840
|
+
await this.provider.connect();
|
|
4841
|
+
logger.success("Database connected successfully");
|
|
4842
|
+
if (this.options.enableShutdownHooks) {
|
|
4843
|
+
this.registerShutdownHooks();
|
|
4844
|
+
}
|
|
4845
|
+
} catch (error) {
|
|
4846
|
+
logger.danger("Database connection failed", error);
|
|
4847
|
+
throw error;
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
async disconnect() {
|
|
4851
|
+
try {
|
|
4852
|
+
await this.provider.disconnect();
|
|
4853
|
+
logger.success("Database connection closed");
|
|
4854
|
+
} catch (error) {
|
|
4855
|
+
logger.danger("Database disconnect failed", error);
|
|
4856
|
+
throw error;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
isConnected() {
|
|
4860
|
+
return this.provider.isConnected();
|
|
4861
|
+
}
|
|
4862
|
+
getConnectionState() {
|
|
4863
|
+
return this.provider.getConnectionState();
|
|
4864
|
+
}
|
|
4865
|
+
registerShutdownHooks() {
|
|
4866
|
+
process.once("SIGINT", async () => {
|
|
4867
|
+
await this.disconnect();
|
|
4868
|
+
process.exit(0);
|
|
4869
|
+
});
|
|
4870
|
+
process.once("SIGTERM", async () => {
|
|
4871
|
+
await this.disconnect();
|
|
4872
|
+
process.exit(0);
|
|
4873
|
+
});
|
|
4874
|
+
}
|
|
4875
|
+
};
|
|
4876
|
+
var MongoDatabaseProvider = class {
|
|
4877
|
+
constructor(config) {
|
|
4878
|
+
this.config = config;
|
|
4879
|
+
}
|
|
4880
|
+
config;
|
|
4881
|
+
async connect() {
|
|
4882
|
+
mongoose.set("strictQuery", this.config.strictQuery ?? true);
|
|
4883
|
+
await mongoose.connect(this.config.uri);
|
|
4884
|
+
}
|
|
4885
|
+
async disconnect() {
|
|
4886
|
+
await mongoose.disconnect();
|
|
4887
|
+
}
|
|
4888
|
+
isConnected() {
|
|
4889
|
+
return mongoose.connection.readyState === 1;
|
|
4890
|
+
}
|
|
4891
|
+
getConnectionState() {
|
|
4892
|
+
switch (mongoose.connection.readyState) {
|
|
4893
|
+
case 0:
|
|
4894
|
+
return "disconnected";
|
|
4895
|
+
case 1:
|
|
4896
|
+
return "connected";
|
|
4897
|
+
case 2:
|
|
4898
|
+
return "connecting";
|
|
4899
|
+
case 3:
|
|
4900
|
+
return "disconnecting";
|
|
4901
|
+
default:
|
|
4902
|
+
return "unknown";
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
};
|
|
4906
|
+
|
|
4907
|
+
// src/errors/appError.ts
|
|
4908
|
+
var AppError = class extends Error {
|
|
4909
|
+
statusCode;
|
|
4910
|
+
status;
|
|
4911
|
+
isOperational;
|
|
4912
|
+
code;
|
|
4913
|
+
details;
|
|
4914
|
+
constructor(message, statusCode = 500, options) {
|
|
4915
|
+
super(message);
|
|
4916
|
+
this.statusCode = statusCode;
|
|
4917
|
+
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
|
|
4918
|
+
this.isOperational = true;
|
|
4919
|
+
this.code = options?.code;
|
|
4920
|
+
this.details = options?.details;
|
|
4921
|
+
Error.captureStackTrace(this, this.constructor);
|
|
4922
|
+
}
|
|
4923
|
+
};
|
|
4924
|
+
|
|
4925
|
+
// src/http/handlers/AsyncHandler.ts
|
|
4926
|
+
var AsyncHandler = (fn) => (req, res, next) => {
|
|
4927
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
4928
|
+
};
|
|
4929
|
+
|
|
4930
|
+
// src/http/query/QueryConstants.ts
|
|
4931
|
+
var QUERY_RESERVED_FIELDS = [
|
|
4932
|
+
"page",
|
|
4933
|
+
"limit",
|
|
4934
|
+
"sort",
|
|
4935
|
+
"fields",
|
|
4936
|
+
"populate"
|
|
4937
|
+
];
|
|
4938
|
+
var DEFAULT_PAGE = 1;
|
|
4939
|
+
var DEFAULT_LIMIT = 100;
|
|
4940
|
+
var DEFAULT_SORT_DIRECTION = "asc";
|
|
4941
|
+
var SUPPORTED_OPERATORS = [
|
|
4942
|
+
"eq",
|
|
4943
|
+
"ne",
|
|
4944
|
+
"gt",
|
|
4945
|
+
"gte",
|
|
4946
|
+
"lt",
|
|
4947
|
+
"lte",
|
|
4948
|
+
"in",
|
|
4949
|
+
"nin",
|
|
4950
|
+
"contains",
|
|
4951
|
+
"startsWith",
|
|
4952
|
+
"endsWith"
|
|
4953
|
+
];
|
|
4954
|
+
|
|
4955
|
+
// src/http/query/ApiFeatures.ts
|
|
4956
|
+
var ApiFeatures = class {
|
|
4957
|
+
static parse(query) {
|
|
4958
|
+
return {
|
|
4959
|
+
filters: this.parseFilters(query),
|
|
4960
|
+
sort: this.parseSort(query),
|
|
4961
|
+
fields: this.parseFields(query),
|
|
4962
|
+
pagination: this.parsePagination(query),
|
|
4963
|
+
populate: this.parsePopulate(query)
|
|
4964
|
+
};
|
|
4965
|
+
}
|
|
4966
|
+
static parseFilters(query) {
|
|
4967
|
+
const filters = [];
|
|
4968
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
4969
|
+
if (QUERY_RESERVED_FIELDS.includes(key)) {
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
const match = key.match(
|
|
4973
|
+
/^(.+)\[(eq|ne|gt|gte|lt|lte|in|nin|contains|startsWith|endsWith)\]$/
|
|
4974
|
+
);
|
|
4975
|
+
if (match) {
|
|
4976
|
+
filters.push({
|
|
4977
|
+
field: match[1],
|
|
4978
|
+
operator: match[2],
|
|
4979
|
+
value
|
|
4980
|
+
});
|
|
4981
|
+
return;
|
|
4982
|
+
}
|
|
4983
|
+
filters.push({
|
|
4984
|
+
field: key,
|
|
4985
|
+
operator: "eq",
|
|
4986
|
+
value
|
|
4987
|
+
});
|
|
4988
|
+
});
|
|
4989
|
+
return filters;
|
|
4990
|
+
}
|
|
4991
|
+
static parseSort(query) {
|
|
4992
|
+
const sort = query.sort?.toString();
|
|
4993
|
+
if (!sort) {
|
|
4994
|
+
return [];
|
|
4995
|
+
}
|
|
4996
|
+
return sort.split(",").filter(Boolean).map((item) => {
|
|
4997
|
+
if (item.startsWith("-")) {
|
|
4998
|
+
return {
|
|
4999
|
+
field: item.substring(1),
|
|
5000
|
+
direction: "desc"
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
5003
|
+
return {
|
|
5004
|
+
field: item,
|
|
5005
|
+
direction: "asc"
|
|
5006
|
+
};
|
|
5007
|
+
});
|
|
5008
|
+
}
|
|
5009
|
+
static parseFields(query) {
|
|
5010
|
+
const fields = query.fields?.toString();
|
|
5011
|
+
if (!fields) {
|
|
5012
|
+
return [];
|
|
5013
|
+
}
|
|
5014
|
+
return fields.split(",").map((field) => field.trim()).filter(Boolean);
|
|
5015
|
+
}
|
|
5016
|
+
static parsePopulate(query) {
|
|
5017
|
+
const populate = query.populate?.toString();
|
|
5018
|
+
if (!populate) {
|
|
5019
|
+
return [];
|
|
5020
|
+
}
|
|
5021
|
+
return populate.split(",").map((field) => field.trim()).filter(Boolean);
|
|
5022
|
+
}
|
|
5023
|
+
static parsePagination(query) {
|
|
5024
|
+
const page = Number(query.page) || DEFAULT_PAGE;
|
|
5025
|
+
const limit = Number(query.limit) || DEFAULT_LIMIT;
|
|
5026
|
+
return {
|
|
5027
|
+
page,
|
|
5028
|
+
limit
|
|
5029
|
+
};
|
|
5030
|
+
}
|
|
5031
|
+
};
|
|
5032
|
+
|
|
5033
|
+
// src/http/responses/ApiResponse.ts
|
|
5034
|
+
var ApiResponse = class _ApiResponse {
|
|
5035
|
+
static send(res, options) {
|
|
5036
|
+
const {
|
|
5037
|
+
statusCode = 200,
|
|
5038
|
+
success = true,
|
|
5039
|
+
message = "Success",
|
|
5040
|
+
data,
|
|
5041
|
+
meta
|
|
5042
|
+
} = options;
|
|
5043
|
+
if (statusCode === 204) {
|
|
5044
|
+
return res.status(204).send();
|
|
5045
|
+
}
|
|
5046
|
+
const body = {
|
|
5047
|
+
success,
|
|
5048
|
+
message
|
|
5049
|
+
};
|
|
5050
|
+
if (data !== void 0) {
|
|
5051
|
+
body.data = data;
|
|
5052
|
+
}
|
|
5053
|
+
if (meta !== void 0) {
|
|
5054
|
+
body.meta = meta;
|
|
5055
|
+
}
|
|
5056
|
+
return res.status(statusCode).json(body);
|
|
5057
|
+
}
|
|
5058
|
+
static ok(res, data, message = "Success", meta) {
|
|
5059
|
+
return _ApiResponse.send(res, {
|
|
5060
|
+
statusCode: 200,
|
|
5061
|
+
success: true,
|
|
5062
|
+
message,
|
|
5063
|
+
data,
|
|
5064
|
+
meta
|
|
5065
|
+
});
|
|
5066
|
+
}
|
|
5067
|
+
static created(res, data, message = "Resource created successfully") {
|
|
5068
|
+
return _ApiResponse.send(res, {
|
|
5069
|
+
statusCode: 201,
|
|
5070
|
+
success: true,
|
|
5071
|
+
message,
|
|
5072
|
+
data
|
|
5073
|
+
});
|
|
5074
|
+
}
|
|
5075
|
+
static accepted(res, data, message = "Request accepted") {
|
|
5076
|
+
return _ApiResponse.send(res, {
|
|
5077
|
+
statusCode: 202,
|
|
5078
|
+
success: true,
|
|
5079
|
+
message,
|
|
5080
|
+
data
|
|
5081
|
+
});
|
|
5082
|
+
}
|
|
5083
|
+
static noContent(res) {
|
|
5084
|
+
return _ApiResponse.send(res, {
|
|
5085
|
+
statusCode: 204
|
|
5086
|
+
});
|
|
5087
|
+
}
|
|
5088
|
+
static error(res, message = "Internal Server Error", statusCode = 500, meta) {
|
|
5089
|
+
return _ApiResponse.send(res, {
|
|
5090
|
+
success: false,
|
|
5091
|
+
statusCode,
|
|
5092
|
+
message,
|
|
5093
|
+
meta
|
|
5094
|
+
});
|
|
5095
|
+
}
|
|
5096
|
+
static paginated(res, data, options, message = "Data fetched successfully") {
|
|
5097
|
+
const meta = {
|
|
5098
|
+
total: options.total,
|
|
5099
|
+
page: options.page,
|
|
5100
|
+
limit: options.limit,
|
|
5101
|
+
totalPages: Math.ceil(options.total / options.limit)
|
|
5102
|
+
};
|
|
5103
|
+
return _ApiResponse.ok(res, data, message, meta);
|
|
5104
|
+
}
|
|
5105
|
+
};
|
|
5106
|
+
|
|
5107
|
+
// src/http/controllers/CrudControllerFactory.ts
|
|
5108
|
+
var CrudControllerFactory = class {
|
|
5109
|
+
static create(repository, options = {}) {
|
|
5110
|
+
const resourceName = options.resourceName ?? "Resource";
|
|
5111
|
+
return {
|
|
5112
|
+
/**
|
|
5113
|
+
* GET /
|
|
5114
|
+
*/
|
|
5115
|
+
getAll: AsyncHandler(async (req, res) => {
|
|
5116
|
+
const parsed = ApiFeatures.parse(req.query);
|
|
5117
|
+
const baseFilter = options.filter?.(req) ?? {};
|
|
5118
|
+
const filter = {
|
|
5119
|
+
...baseFilter
|
|
5120
|
+
};
|
|
5121
|
+
parsed.filters.forEach((item) => {
|
|
5122
|
+
if (item.operator === "eq") {
|
|
5123
|
+
filter[item.field] = item.value;
|
|
5124
|
+
}
|
|
5125
|
+
});
|
|
5126
|
+
const queryOptions = {
|
|
5127
|
+
limit: parsed.pagination.limit,
|
|
5128
|
+
skip: (parsed.pagination.page - 1) * parsed.pagination.limit,
|
|
5129
|
+
populate: parsed.populate
|
|
5130
|
+
};
|
|
5131
|
+
if (parsed.sort.length > 0) {
|
|
5132
|
+
queryOptions.sort = Object.fromEntries(
|
|
5133
|
+
parsed.sort.map((item) => [
|
|
5134
|
+
item.field,
|
|
5135
|
+
item.direction === "asc" ? 1 : -1
|
|
5136
|
+
])
|
|
5137
|
+
);
|
|
5138
|
+
}
|
|
5139
|
+
if (parsed.fields.length > 0) {
|
|
5140
|
+
queryOptions.select = parsed.fields;
|
|
5141
|
+
}
|
|
5142
|
+
const docs = await repository.findAll(filter, queryOptions);
|
|
5143
|
+
const total = await repository.count(filter);
|
|
5144
|
+
return ApiResponse.paginated(
|
|
5145
|
+
res,
|
|
5146
|
+
docs,
|
|
5147
|
+
{
|
|
5148
|
+
total,
|
|
5149
|
+
page: parsed.pagination.page,
|
|
5150
|
+
limit: parsed.pagination.limit
|
|
5151
|
+
},
|
|
5152
|
+
`${resourceName} list fetched successfully`
|
|
5153
|
+
);
|
|
5154
|
+
}),
|
|
5155
|
+
/**
|
|
5156
|
+
* GET /:id
|
|
5157
|
+
*/
|
|
5158
|
+
getOne: AsyncHandler(async (req, res) => {
|
|
5159
|
+
const id = String(req.params.id);
|
|
5160
|
+
const doc = await repository.findById(id);
|
|
5161
|
+
if (!doc) {
|
|
5162
|
+
throw new AppError(`${resourceName} not found`, 404, {
|
|
5163
|
+
code: "RESOURCE_NOT_FOUND"
|
|
5164
|
+
});
|
|
5165
|
+
}
|
|
5166
|
+
return ApiResponse.ok(res, doc, `${resourceName} fetched successfully`);
|
|
5167
|
+
}),
|
|
5168
|
+
/**
|
|
5169
|
+
* POST /
|
|
5170
|
+
*/
|
|
5171
|
+
create: AsyncHandler(async (req, res) => {
|
|
5172
|
+
const doc = await repository.create(req.body);
|
|
5173
|
+
return ApiResponse.created(
|
|
5174
|
+
res,
|
|
5175
|
+
doc,
|
|
5176
|
+
`${resourceName} created successfully`
|
|
5177
|
+
);
|
|
5178
|
+
}),
|
|
5179
|
+
/**
|
|
5180
|
+
* PATCH /:id
|
|
5181
|
+
*/
|
|
5182
|
+
update: AsyncHandler(async (req, res) => {
|
|
5183
|
+
const id = String(req.params.id);
|
|
5184
|
+
const doc = await repository.updateById(id, req.body);
|
|
5185
|
+
if (!doc) {
|
|
5186
|
+
throw new AppError(`${resourceName} not found`, 404, {
|
|
5187
|
+
code: "RESOURCE_NOT_FOUND"
|
|
5188
|
+
});
|
|
5189
|
+
}
|
|
5190
|
+
return ApiResponse.ok(res, doc, `${resourceName} updated successfully`);
|
|
5191
|
+
}),
|
|
5192
|
+
/**
|
|
5193
|
+
* DELETE /:id
|
|
5194
|
+
*/
|
|
5195
|
+
delete: AsyncHandler(async (req, res) => {
|
|
5196
|
+
const id = String(req.params.id);
|
|
5197
|
+
const doc = await repository.deleteById(id);
|
|
5198
|
+
if (!doc) {
|
|
5199
|
+
throw new AppError(`${resourceName} not found`, 404, {
|
|
5200
|
+
code: "RESOURCE_NOT_FOUND"
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5203
|
+
return ApiResponse.noContent(res);
|
|
5204
|
+
})
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
5207
|
+
};
|
|
5208
|
+
|
|
5209
|
+
// src/http/middleware/AuthMiddleware.ts
|
|
5091
5210
|
var authMiddleware = (_req, _res, next) => {
|
|
5092
5211
|
next();
|
|
5093
5212
|
};
|
|
5094
5213
|
|
|
5095
|
-
// src/
|
|
5096
|
-
var
|
|
5214
|
+
// src/http/middleware/ErrorMiddleware.ts
|
|
5215
|
+
var handleCastError = (err) => new AppError(`Invalid ${err.path}: ${err.value}`, 400, {
|
|
5097
5216
|
code: "INVALID_ID"
|
|
5098
5217
|
});
|
|
5099
|
-
var
|
|
5218
|
+
var handleDuplicateKey = (err) => {
|
|
5100
5219
|
const field = err.keyValue ? Object.keys(err.keyValue)[0] : "field";
|
|
5101
5220
|
return new AppError(`${field} already exists`, 400, {
|
|
5102
5221
|
code: "DUPLICATE_FIELD"
|
|
5103
5222
|
});
|
|
5104
5223
|
};
|
|
5105
|
-
var
|
|
5224
|
+
var handleValidationError = (err) => {
|
|
5106
5225
|
const errors = Object.entries(err.errors).reduce(
|
|
5107
5226
|
(acc, [key, value]) => {
|
|
5108
5227
|
acc[key] = value.message;
|
|
@@ -5113,12 +5232,11 @@ var handleValidationErrorDB = (err) => {
|
|
|
5113
5232
|
return new AppError("Validation failed", 400, {
|
|
5114
5233
|
code: "VALIDATION_ERROR",
|
|
5115
5234
|
details: errors
|
|
5116
|
-
// structured field errors
|
|
5117
5235
|
});
|
|
5118
5236
|
};
|
|
5119
|
-
var handleJWTError = () => new AppError("Invalid token
|
|
5120
|
-
var handleJWTExpiredError = () => new AppError("
|
|
5121
|
-
var
|
|
5237
|
+
var handleJWTError = () => new AppError("Invalid token", 401);
|
|
5238
|
+
var handleJWTExpiredError = () => new AppError("Token expired", 401);
|
|
5239
|
+
var ErrorMiddleware = (isProduction = false) => (err, _req, res, _next) => {
|
|
5122
5240
|
let error;
|
|
5123
5241
|
if (err instanceof AppError) {
|
|
5124
5242
|
error = err;
|
|
@@ -5130,15 +5248,15 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
|
|
|
5130
5248
|
error.isOperational = false;
|
|
5131
5249
|
}
|
|
5132
5250
|
if (err && err.name === "CastError") {
|
|
5133
|
-
error =
|
|
5251
|
+
error = handleCastError(err);
|
|
5134
5252
|
}
|
|
5135
5253
|
if (err && typeof err === "object" && err.code === 11e3) {
|
|
5136
|
-
error =
|
|
5254
|
+
error = handleDuplicateKey(
|
|
5137
5255
|
err
|
|
5138
5256
|
);
|
|
5139
5257
|
}
|
|
5140
5258
|
if (err && err.name === "ValidationError") {
|
|
5141
|
-
error =
|
|
5259
|
+
error = handleValidationError(err);
|
|
5142
5260
|
}
|
|
5143
5261
|
if (err?.name === "JsonWebTokenError") {
|
|
5144
5262
|
error = handleJWTError();
|
|
@@ -5149,9 +5267,11 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
|
|
|
5149
5267
|
if (!error.isOperational) {
|
|
5150
5268
|
logger.danger("Programming error", err);
|
|
5151
5269
|
} else {
|
|
5152
|
-
logger.danger("Operational error", {
|
|
5270
|
+
logger.danger("Operational error", {
|
|
5271
|
+
message: error.message
|
|
5272
|
+
});
|
|
5153
5273
|
}
|
|
5154
|
-
const
|
|
5274
|
+
const response = {
|
|
5155
5275
|
success: false,
|
|
5156
5276
|
message: error.isOperational ? error.message : "Something went wrong",
|
|
5157
5277
|
...error.details && {
|
|
@@ -5160,32 +5280,144 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
|
|
|
5160
5280
|
}
|
|
5161
5281
|
}
|
|
5162
5282
|
};
|
|
5163
|
-
if (!
|
|
5164
|
-
return
|
|
5283
|
+
if (!isProduction) {
|
|
5284
|
+
return ApiResponse.send(res, {
|
|
5165
5285
|
statusCode: error.statusCode,
|
|
5166
|
-
...
|
|
5286
|
+
...response,
|
|
5167
5287
|
data: {
|
|
5168
5288
|
stack: err?.stack
|
|
5169
5289
|
}
|
|
5170
5290
|
});
|
|
5171
5291
|
}
|
|
5172
|
-
return
|
|
5292
|
+
return ApiResponse.send(res, {
|
|
5173
5293
|
statusCode: error.statusCode,
|
|
5174
|
-
...
|
|
5294
|
+
...response
|
|
5175
5295
|
});
|
|
5176
5296
|
};
|
|
5177
5297
|
|
|
5178
|
-
// src/
|
|
5298
|
+
// src/http/validation/Validator.ts
|
|
5299
|
+
var isJoiSchema = (schema) => {
|
|
5300
|
+
return typeof schema === "object" && schema !== null && "validate" in schema;
|
|
5301
|
+
};
|
|
5302
|
+
var isZodSchema = (schema) => {
|
|
5303
|
+
return typeof schema === "object" && schema !== null && "safeParse" in schema;
|
|
5304
|
+
};
|
|
5305
|
+
|
|
5306
|
+
// src/http/validation/JoiValidator.ts
|
|
5307
|
+
var JoiValidator = class {
|
|
5308
|
+
constructor(schema) {
|
|
5309
|
+
this.schema = schema;
|
|
5310
|
+
}
|
|
5311
|
+
schema;
|
|
5312
|
+
validate(data) {
|
|
5313
|
+
const result = this.schema.validate(data, {
|
|
5314
|
+
abortEarly: false,
|
|
5315
|
+
stripUnknown: true
|
|
5316
|
+
});
|
|
5317
|
+
if (result.error) {
|
|
5318
|
+
const errors = {};
|
|
5319
|
+
result.error.details.forEach((detail) => {
|
|
5320
|
+
const field = detail.path.join(".");
|
|
5321
|
+
errors[field] = detail.message;
|
|
5322
|
+
});
|
|
5323
|
+
return {
|
|
5324
|
+
success: false,
|
|
5325
|
+
errors
|
|
5326
|
+
};
|
|
5327
|
+
}
|
|
5328
|
+
return {
|
|
5329
|
+
success: true,
|
|
5330
|
+
data: result.value
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5333
|
+
};
|
|
5334
|
+
|
|
5335
|
+
// src/http/validation/ZodValidator.ts
|
|
5336
|
+
var ZodValidator = class {
|
|
5337
|
+
constructor(schema) {
|
|
5338
|
+
this.schema = schema;
|
|
5339
|
+
}
|
|
5340
|
+
schema;
|
|
5341
|
+
validate(data) {
|
|
5342
|
+
const result = this.schema.safeParse(data);
|
|
5343
|
+
if (!result.success) {
|
|
5344
|
+
const errors = {};
|
|
5345
|
+
result.error.issues.forEach((issue) => {
|
|
5346
|
+
const field = issue.path.join(".");
|
|
5347
|
+
errors[field] = issue.message;
|
|
5348
|
+
});
|
|
5349
|
+
return {
|
|
5350
|
+
success: false,
|
|
5351
|
+
errors
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
return {
|
|
5355
|
+
success: true,
|
|
5356
|
+
data: result.data
|
|
5357
|
+
};
|
|
5358
|
+
}
|
|
5359
|
+
};
|
|
5360
|
+
|
|
5361
|
+
// src/http/middleware/ValidationMiddleware.ts
|
|
5179
5362
|
var validate = (schema) => (req, _res, next) => {
|
|
5180
|
-
|
|
5181
|
-
if (
|
|
5182
|
-
|
|
5363
|
+
let result;
|
|
5364
|
+
if (isJoiSchema(schema)) {
|
|
5365
|
+
result = new JoiValidator(schema).validate(req.body);
|
|
5366
|
+
} else if (isZodSchema(schema)) {
|
|
5367
|
+
result = new ZodValidator(schema).validate(req.body);
|
|
5368
|
+
} else {
|
|
5369
|
+
return next(new AppError("Unsupported validation schema", 500));
|
|
5370
|
+
}
|
|
5371
|
+
if (!result.success) {
|
|
5372
|
+
return next(
|
|
5373
|
+
new AppError("Validation failed", 400, {
|
|
5374
|
+
code: "VALIDATION_ERROR",
|
|
5375
|
+
details: result.errors
|
|
5376
|
+
})
|
|
5377
|
+
);
|
|
5183
5378
|
}
|
|
5379
|
+
req.body = result.data;
|
|
5184
5380
|
next();
|
|
5185
5381
|
};
|
|
5186
5382
|
|
|
5187
|
-
// src/
|
|
5188
|
-
|
|
5383
|
+
// src/http/utils/PickFields.ts
|
|
5384
|
+
function pickFields(obj, fields, options = {}) {
|
|
5385
|
+
const removeUndefined = options.removeUndefined ?? false;
|
|
5386
|
+
const removeNull = options.removeNull ?? false;
|
|
5387
|
+
const removeEmptyString = options.removeEmptyString ?? false;
|
|
5388
|
+
const strict = options.strict ?? false;
|
|
5389
|
+
const defaults = options.defaults ?? {};
|
|
5390
|
+
const rename = options.rename ?? {};
|
|
5391
|
+
const transform = options.transform ?? {};
|
|
5392
|
+
const result = {};
|
|
5393
|
+
if (strict) {
|
|
5394
|
+
for (const key of Object.keys(obj)) {
|
|
5395
|
+
if (!fields.includes(key)) {
|
|
5396
|
+
throw new Error(`Unknown field: ${key}`);
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
for (const field of fields) {
|
|
5401
|
+
let value = obj[field];
|
|
5402
|
+
if (value === void 0 && field in defaults) {
|
|
5403
|
+
value = defaults[field];
|
|
5404
|
+
}
|
|
5405
|
+
if (removeUndefined && value === void 0) continue;
|
|
5406
|
+
if (removeNull && value === null) continue;
|
|
5407
|
+
if (removeEmptyString && value === "") continue;
|
|
5408
|
+
if (transform[field]) {
|
|
5409
|
+
value = transform[field](value);
|
|
5410
|
+
}
|
|
5411
|
+
const outputKey = rename[field] ?? field;
|
|
5412
|
+
if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
|
|
5413
|
+
result[outputKey] = value;
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
return result;
|
|
5417
|
+
}
|
|
5418
|
+
|
|
5419
|
+
// src/repositories/providers/MongoRepository.ts
|
|
5420
|
+
var MongoRepository = class {
|
|
5189
5421
|
model;
|
|
5190
5422
|
constructor(model) {
|
|
5191
5423
|
this.model = model;
|
|
@@ -5739,6 +5971,6 @@ function sleep(ms) {
|
|
|
5739
5971
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5740
5972
|
}
|
|
5741
5973
|
|
|
5742
|
-
export {
|
|
5974
|
+
export { AVLTree, AdjacencyList, AdjacencyMatrix, ApiFeatures, ApiResponse, AppError, AsyncHandler, AzureBlobStorageService, BPlusTree, BTree, BinaryHeap, BinarySearchTree, BinaryTree, BloomFilter, CircularArray, CircularLinkedList, CircularQueue, CloudinaryService, ConsistentHash, CountMinSketch, CrudControllerFactory, DEFAULT_LIMIT, DEFAULT_PAGE, DEFAULT_SORT_DIRECTION, DatabaseManager, Deque, DirectedGraph, DisjointSetUnion, DoublyLinkedList, DynamicArray, EmailService, ErrorMiddleware, FenwickTree, FibNode, FibonacciHeap, Graph, HashMap, HashSet, HyperLogLog, IntervalTree, JWTService, JoiValidator, KDTree, LFUCache, LRUCache, MaxHeap, MaxStack, MinHeap, MinStack, MongoDatabaseProvider, MongoRepository, MulterFileHandlerService, MultiSet, Node, OrderedSet, PairingHeap, PairingNode, PriorityQueue, QUERY_RESERVED_FIELDS, QuadTree, Queue, RadixTree, RedBlackTree, SMTPProvider, SUPPORTED_OPERATORS, SegmentTree, Set2 as Set, SinglyLinkedList, SparseTable, SplayTree, Stack, StaticArray, SuffixArray, SuffixTree, TemplateEngine, TernarySearchTree, TreeNode, Trie, ZodValidator, authMiddleware, createAllowedOrigins, createCorsOptions, days, errorToString, hours, isJoiSchema, isZodSchema, loadEnv, logger, milliseconds, minutes, pickFields, seconds, sleep, toHours, toMinutes, toSeconds, validate };
|
|
5743
5975
|
//# sourceMappingURL=index.js.map
|
|
5744
5976
|
//# sourceMappingURL=index.js.map
|