@codisolutions23/node-utils 1.0.0 → 1.0.1
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.d.mts +143 -0
- package/dist/index.d.ts +143 -0
- package/dist/index.js +758 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +704 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/tsconfig.json +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __export = (target, all) => {
|
|
23
|
+
for (var name in all)
|
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
25
|
+
};
|
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
28
|
+
for (let key of __getOwnPropNames(from))
|
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
30
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
31
|
+
}
|
|
32
|
+
return to;
|
|
33
|
+
};
|
|
34
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
35
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
36
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
37
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
38
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
39
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
40
|
+
mod
|
|
41
|
+
));
|
|
42
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
43
|
+
var __async = (__this, __arguments, generator) => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
var fulfilled = (value) => {
|
|
46
|
+
try {
|
|
47
|
+
step(generator.next(value));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
reject(e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var rejected = (value) => {
|
|
53
|
+
try {
|
|
54
|
+
step(generator.throw(value));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
60
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var index_exports = {};
|
|
66
|
+
__export(index_exports, {
|
|
67
|
+
BadRequestError: () => BadRequestError,
|
|
68
|
+
ForbiddenError: () => ForbiddenError,
|
|
69
|
+
HttpError: () => HttpError,
|
|
70
|
+
InternalServerError: () => InternalServerError,
|
|
71
|
+
NotFoundError: () => NotFoundError,
|
|
72
|
+
UnauthorizedError: () => UnauthorizedError,
|
|
73
|
+
UnprocessableEntityError: () => UnprocessableEntityError,
|
|
74
|
+
authenticateJWT: () => authenticateJWT,
|
|
75
|
+
buildCacheKey: () => buildCacheKey,
|
|
76
|
+
comparePasswords: () => comparePasswords,
|
|
77
|
+
errorHandler: () => errorHandler,
|
|
78
|
+
getTemplatePath: () => getTemplatePath,
|
|
79
|
+
hashPassword: () => hashPassword,
|
|
80
|
+
initRedisClient: () => initRedisClient,
|
|
81
|
+
logger: () => logger,
|
|
82
|
+
paginate: () => paginate,
|
|
83
|
+
renderHandlebarsTemplate: () => renderHandlebarsTemplate,
|
|
84
|
+
signJwtToken: () => signJwtToken,
|
|
85
|
+
toObjectId: () => toObjectId,
|
|
86
|
+
useAtlas: () => useAtlas,
|
|
87
|
+
useCache: () => useCache,
|
|
88
|
+
useMailer: () => useMailer,
|
|
89
|
+
useRedis: () => useRedis,
|
|
90
|
+
useRedisClient: () => useRedisClient,
|
|
91
|
+
useS3: () => useS3
|
|
92
|
+
});
|
|
93
|
+
module.exports = __toCommonJS(index_exports);
|
|
94
|
+
|
|
95
|
+
// src/middleware/auth.middleware.ts
|
|
96
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
97
|
+
|
|
98
|
+
// src/utils/logger.ts
|
|
99
|
+
var winston = __toESM(require("winston"));
|
|
100
|
+
var transports2 = [
|
|
101
|
+
new winston.transports.File({ filename: "error.log", level: "error" }),
|
|
102
|
+
new winston.transports.File({ filename: "combined.log" })
|
|
103
|
+
];
|
|
104
|
+
var logger = winston.createLogger({
|
|
105
|
+
level: "info",
|
|
106
|
+
format: winston.format.combine(
|
|
107
|
+
winston.format.timestamp(),
|
|
108
|
+
winston.format.json()
|
|
109
|
+
),
|
|
110
|
+
transports: transports2
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/utils/http-error.ts
|
|
114
|
+
var HttpError = class extends Error {
|
|
115
|
+
constructor(message, statusCode, isOperational = true) {
|
|
116
|
+
super(message);
|
|
117
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
118
|
+
this.statusCode = statusCode;
|
|
119
|
+
this.isOperational = isOperational;
|
|
120
|
+
if (Error.captureStackTrace) {
|
|
121
|
+
Error.captureStackTrace(this, this.constructor);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var BadRequestError = class extends HttpError {
|
|
126
|
+
constructor(message = "The request could not be processed. Please review your input and try again.") {
|
|
127
|
+
super(message, 400, true);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var UnauthorizedError = class extends HttpError {
|
|
131
|
+
constructor(message = "Authentication is required to access this resource.") {
|
|
132
|
+
super(message, 401, true);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var ForbiddenError = class extends HttpError {
|
|
136
|
+
constructor(message = "You do not have the necessary permissions to perform this action.") {
|
|
137
|
+
super(message, 403, true);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var NotFoundError = class extends HttpError {
|
|
141
|
+
constructor(message = "The requested resource could not be found.") {
|
|
142
|
+
super(message, 404, true);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var UnprocessableEntityError = class extends HttpError {
|
|
146
|
+
constructor(message = "The request could not be completed due to invalid or incomplete information.") {
|
|
147
|
+
super(message, 422, true);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var InternalServerError = class extends HttpError {
|
|
151
|
+
constructor(message = "An internal server error occurred. Please try again later.") {
|
|
152
|
+
super(message, 500, true);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/middleware/auth.middleware.ts
|
|
157
|
+
function authenticateJWT(secretKey = "") {
|
|
158
|
+
return (req, res, next) => {
|
|
159
|
+
const authHeader = req.headers.authorization;
|
|
160
|
+
const jwtToken = authHeader && authHeader.split(" ")[1];
|
|
161
|
+
if (!jwtToken) {
|
|
162
|
+
logger.error("No access token provided in the request.");
|
|
163
|
+
return next(
|
|
164
|
+
new UnauthorizedError("Access token is required to proceed.")
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (!secretKey) {
|
|
168
|
+
logger.error("JWT secret key is not set in the configuration.");
|
|
169
|
+
return next(
|
|
170
|
+
new UnauthorizedError(
|
|
171
|
+
"Authentication service is not properly configured."
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
import_jsonwebtoken.default.verify(jwtToken, secretKey, (error, decodedUser) => {
|
|
176
|
+
if (error) {
|
|
177
|
+
logger.error("Failed to verify access token.", error);
|
|
178
|
+
return next(
|
|
179
|
+
new UnauthorizedError(
|
|
180
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
req.user = decodedUser;
|
|
185
|
+
next();
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/middleware/error-handler.middleware.ts
|
|
191
|
+
var errorHandler = (error, req, res, next) => {
|
|
192
|
+
if (error.isOperational) {
|
|
193
|
+
res.status(error.statusCode).json({ status: "error", message: error.message });
|
|
194
|
+
} else {
|
|
195
|
+
logger.error({ message: error.message });
|
|
196
|
+
res.status(500).json({ status: "error", message: new InternalServerError().message });
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/utils/atlas.ts
|
|
202
|
+
var import_mongodb = require("mongodb");
|
|
203
|
+
var useAtlas = class {
|
|
204
|
+
static connect(config2) {
|
|
205
|
+
return __async(this, null, function* () {
|
|
206
|
+
if (this.mongoClient) {
|
|
207
|
+
logger.warn(
|
|
208
|
+
"[MongoDB][Connect] Client is already connected. Skipping new connection."
|
|
209
|
+
);
|
|
210
|
+
throw new BadRequestError("A MongoDB connection is already established.");
|
|
211
|
+
}
|
|
212
|
+
const { db, uri } = config2;
|
|
213
|
+
this.mongoClient = new import_mongodb.MongoClient(uri, {
|
|
214
|
+
maxPoolSize: 10,
|
|
215
|
+
maxIdleTimeMS: 6e4,
|
|
216
|
+
connectTimeoutMS: 6e4
|
|
217
|
+
});
|
|
218
|
+
try {
|
|
219
|
+
yield this.mongoClient.connect();
|
|
220
|
+
this.mongoDb = this.mongoClient.db(db);
|
|
221
|
+
logger.info(
|
|
222
|
+
`[MongoDB][Connect] Connected to database "${this.mongoDb.databaseName}".`
|
|
223
|
+
);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
this.mongoClient = null;
|
|
226
|
+
logger.error(
|
|
227
|
+
`[MongoDB][Connect] Failed to connect: ${error instanceof Error ? error.message : error}`
|
|
228
|
+
);
|
|
229
|
+
throw new InternalServerError(
|
|
230
|
+
"Failed to connect to the database. Please try again later."
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
static getClient() {
|
|
236
|
+
if (!this.mongoClient) {
|
|
237
|
+
logger.warn(`[MongoDB][GetClient] Client is not initialized.`);
|
|
238
|
+
throw new NotFoundError("MongoDB client is not initialized.");
|
|
239
|
+
}
|
|
240
|
+
return this.mongoClient;
|
|
241
|
+
}
|
|
242
|
+
static getDb() {
|
|
243
|
+
if (!this.mongoDb) {
|
|
244
|
+
logger.warn(`[MongoDB][GetDb] Database instance is not available.`);
|
|
245
|
+
throw new NotFoundError("MongoDB database instance is not available.");
|
|
246
|
+
}
|
|
247
|
+
return this.mongoDb;
|
|
248
|
+
}
|
|
249
|
+
static close() {
|
|
250
|
+
return __async(this, null, function* () {
|
|
251
|
+
if (this.mongoClient) {
|
|
252
|
+
try {
|
|
253
|
+
yield this.mongoClient.close();
|
|
254
|
+
logger.info(`[MongoDB][Close] Connection closed.`);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
logger.error(
|
|
257
|
+
`[MongoDB][Close] Error closing connection: ${error instanceof Error ? error.message : error}`
|
|
258
|
+
);
|
|
259
|
+
throw new InternalServerError("Failed to close MongoDB connection.");
|
|
260
|
+
} finally {
|
|
261
|
+
this.mongoClient = null;
|
|
262
|
+
this.mongoDb = null;
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
logger.warn(`[MongoDB][Close] No client to disconnect.`);
|
|
266
|
+
throw new BadRequestError("No MongoDB connection exists to close.");
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
useAtlas.mongoClient = null;
|
|
272
|
+
useAtlas.mongoDb = null;
|
|
273
|
+
|
|
274
|
+
// src/utils/cache-key.ts
|
|
275
|
+
function buildCacheKey(prefix, values) {
|
|
276
|
+
const query = Object.entries(values).sort(([a], [b]) => a.localeCompare(b)).map(
|
|
277
|
+
([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`
|
|
278
|
+
).join("&");
|
|
279
|
+
return `${prefix}:${query}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/utils/ioredis.ts
|
|
283
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
284
|
+
var redisClient = null;
|
|
285
|
+
function useRedis() {
|
|
286
|
+
function initialize(options) {
|
|
287
|
+
var _a, _b, _c, _d;
|
|
288
|
+
if (redisClient) {
|
|
289
|
+
logger.info("[Redis][Init] Redis connection is already established.");
|
|
290
|
+
return redisClient;
|
|
291
|
+
}
|
|
292
|
+
options.host = (_a = options.host) != null ? _a : "localhost";
|
|
293
|
+
options.port = (_b = options.port) != null ? _b : 6379;
|
|
294
|
+
options.username = (_c = options.username) != null ? _c : "default";
|
|
295
|
+
options.password = (_d = options.password) != null ? _d : "";
|
|
296
|
+
redisClient = new import_ioredis.default(__spreadValues(__spreadValues({
|
|
297
|
+
host: options.host,
|
|
298
|
+
port: options.port
|
|
299
|
+
}, options.username && { username: options.username }), options.password && { password: options.password }));
|
|
300
|
+
redisClient.on("connect", () => {
|
|
301
|
+
logger.info(
|
|
302
|
+
`[Redis][Connect] Redis client connected at ${options.host}:${options.port}.`
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
redisClient.on("error", (error) => {
|
|
306
|
+
logger.error(
|
|
307
|
+
`[Redis][Error] Failed to connect: ${error instanceof Error ? error.message : error}`
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
return redisClient;
|
|
311
|
+
}
|
|
312
|
+
function getClient() {
|
|
313
|
+
if (!redisClient) {
|
|
314
|
+
logger.error(
|
|
315
|
+
"[Redis][GetClient] Redis connection has not been initialized."
|
|
316
|
+
);
|
|
317
|
+
throw new BadRequestError(
|
|
318
|
+
"Redis connection is not initialized. Please call initialize() first."
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return redisClient;
|
|
322
|
+
}
|
|
323
|
+
function disconnect() {
|
|
324
|
+
if (redisClient) {
|
|
325
|
+
redisClient.quit();
|
|
326
|
+
logger.info("[Redis][Disconnect] Redis connection has been closed.");
|
|
327
|
+
redisClient = null;
|
|
328
|
+
} else {
|
|
329
|
+
logger.warn("[Redis][Disconnect] No Redis client to disconnect.");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
initialize,
|
|
334
|
+
getClient,
|
|
335
|
+
disconnect
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/utils/cache.ts
|
|
340
|
+
var DEFAULT_TTL = 300;
|
|
341
|
+
function useCache() {
|
|
342
|
+
const redisClient3 = useRedis().getClient();
|
|
343
|
+
function get(cacheKey) {
|
|
344
|
+
return __async(this, null, function* () {
|
|
345
|
+
try {
|
|
346
|
+
const value = yield redisClient3.get(cacheKey);
|
|
347
|
+
return value ? JSON.parse(value) : null;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
logger.error(
|
|
350
|
+
`[Cache][Get] Failed to retrieve key "${cacheKey}": ${error instanceof Error ? error.message : error}`
|
|
351
|
+
);
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function set(_0, _1) {
|
|
357
|
+
return __async(this, arguments, function* (cacheKey, data, ttl = DEFAULT_TTL, group) {
|
|
358
|
+
try {
|
|
359
|
+
yield redisClient3.set(cacheKey, JSON.stringify(data), "EX", ttl);
|
|
360
|
+
logger.info(`[Cache][Set] Stored key "${cacheKey}" with TTL ${ttl}s.`);
|
|
361
|
+
if (group) yield redisClient3.sadd(`cache:group:${group}`, cacheKey);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
logger.error(
|
|
364
|
+
`[Cache][Set] Failed to store key "${cacheKey}": ${error instanceof Error ? error.message : error}`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function remove(cacheKey) {
|
|
370
|
+
return __async(this, null, function* () {
|
|
371
|
+
try {
|
|
372
|
+
yield redisClient3.del(cacheKey);
|
|
373
|
+
logger.info(`[Cache][Remove] Key "${cacheKey}" has been deleted.`);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger.error(
|
|
376
|
+
`[Cache][Remove] Failed to delete key "${cacheKey}": ${error instanceof Error ? error.message : error}`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function clearGroup(group) {
|
|
382
|
+
return __async(this, null, function* () {
|
|
383
|
+
try {
|
|
384
|
+
const keys = yield redisClient3.smembers(`cache:group:${group}`);
|
|
385
|
+
if (keys.length) yield redisClient3.del(...keys);
|
|
386
|
+
yield redisClient3.del(`cache:group:${group}`);
|
|
387
|
+
logger.info(`[Cache][ClearGroup] Cleared group "${group}" and its keys.`);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
logger.error(
|
|
390
|
+
`[Cache][ClearGroup] Failed to clear group "${group}": ${error instanceof Error ? error.message : error}`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
get,
|
|
397
|
+
set,
|
|
398
|
+
remove,
|
|
399
|
+
clearGroup
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/utils/get-template-path.ts
|
|
404
|
+
var import_path = __toESM(require("path"));
|
|
405
|
+
function getTemplatePath(directory, filePath) {
|
|
406
|
+
const ext = ".hbs";
|
|
407
|
+
const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;
|
|
408
|
+
return import_path.default.resolve(directory, file);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/utils/handlebars-compiler.ts
|
|
412
|
+
var import_handlebars = __toESM(require("handlebars"));
|
|
413
|
+
var import_fs = require("fs");
|
|
414
|
+
var handlebarsTemplateCache = /* @__PURE__ */ new Map();
|
|
415
|
+
function renderHandlebarsTemplate(_0) {
|
|
416
|
+
return __async(this, arguments, function* ({
|
|
417
|
+
context = {},
|
|
418
|
+
filePath
|
|
419
|
+
}) {
|
|
420
|
+
try {
|
|
421
|
+
let compiledTemplate = handlebarsTemplateCache.get(filePath);
|
|
422
|
+
if (!compiledTemplate) {
|
|
423
|
+
logger.info(
|
|
424
|
+
`[Template][Compile] Compiling and caching template from "${filePath}".`
|
|
425
|
+
);
|
|
426
|
+
const fileContent = yield import_fs.promises.readFile(filePath, "utf8");
|
|
427
|
+
compiledTemplate = import_handlebars.default.compile(fileContent);
|
|
428
|
+
handlebarsTemplateCache.set(filePath, compiledTemplate);
|
|
429
|
+
} else {
|
|
430
|
+
logger.info(`[Template][Cache] Using cached template for "${filePath}".`);
|
|
431
|
+
}
|
|
432
|
+
return compiledTemplate(context);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
logger.error(
|
|
435
|
+
`[Template][Render] Failed to render template "${filePath}": ${error instanceof Error ? error.message : error}`
|
|
436
|
+
);
|
|
437
|
+
throw new InternalServerError("Failed to render Handlebars template.");
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/utils/jwt.ts
|
|
443
|
+
var import_jsonwebtoken2 = __toESM(require("jsonwebtoken"));
|
|
444
|
+
function signJwtToken({
|
|
445
|
+
payload = {},
|
|
446
|
+
secretKey = "",
|
|
447
|
+
signOptions = {}
|
|
448
|
+
}) {
|
|
449
|
+
if (!secretKey) {
|
|
450
|
+
logger.error(`[JWT][Sign] Secret key is missing. Cannot sign token.`);
|
|
451
|
+
throw new BadRequestError("A JWT secret key must be provided.");
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
logger.info(`[JWT][Sign] Signing JWT token.`);
|
|
455
|
+
return import_jsonwebtoken2.default.sign(payload, secretKey, signOptions);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
logger.error(
|
|
458
|
+
`[JWT][Sign] Failed to sign JWT token: ${error instanceof Error ? error.message : error}`
|
|
459
|
+
);
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/utils/mailer.ts
|
|
465
|
+
var import_nodemailer = require("nodemailer");
|
|
466
|
+
var useMailer = class {
|
|
467
|
+
constructor(config2) {
|
|
468
|
+
this.config = config2;
|
|
469
|
+
this.transporter = (0, import_nodemailer.createTransport)({
|
|
470
|
+
host: config2.host,
|
|
471
|
+
port: config2.port,
|
|
472
|
+
secure: config2.secure,
|
|
473
|
+
auth: { user: config2.email, pass: config2.password }
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
sendMail(_0) {
|
|
477
|
+
return __async(this, arguments, function* ({
|
|
478
|
+
sender,
|
|
479
|
+
to,
|
|
480
|
+
subject,
|
|
481
|
+
text,
|
|
482
|
+
html
|
|
483
|
+
}) {
|
|
484
|
+
const from = sender ? `${sender} <${this.config.email}>` : this.config.email;
|
|
485
|
+
const mailOptions = __spreadValues(__spreadValues({
|
|
486
|
+
from,
|
|
487
|
+
to,
|
|
488
|
+
subject
|
|
489
|
+
}, text && { text }), html && { html });
|
|
490
|
+
try {
|
|
491
|
+
yield this.transporter.sendMail(mailOptions);
|
|
492
|
+
logger.info(
|
|
493
|
+
`[Mailer][Send] Email sent to "${to}" with subject "${subject}".`
|
|
494
|
+
);
|
|
495
|
+
return "Mail sent successfully.";
|
|
496
|
+
} catch (error) {
|
|
497
|
+
logger.error(
|
|
498
|
+
`[Mailer][Send] Failed to send email to "${to}" with subject "${subject}": ${error instanceof Error ? error.message : error}`
|
|
499
|
+
);
|
|
500
|
+
throw new InternalServerError("Failed to send email.");
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/utils/objectid-converter.ts
|
|
507
|
+
var import_mongodb2 = require("mongodb");
|
|
508
|
+
function toObjectId(id) {
|
|
509
|
+
if (!id) {
|
|
510
|
+
logger.error(
|
|
511
|
+
`[ObjectId][Convert] No value provided for MongoDB ObjectId conversion.`
|
|
512
|
+
);
|
|
513
|
+
throw new BadRequestError(
|
|
514
|
+
"A value must be provided for ObjectId conversion."
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
if (id instanceof import_mongodb2.ObjectId) return id;
|
|
518
|
+
if (!/^[0-9a-fA-F]{24}$/.test(id)) {
|
|
519
|
+
logger.error(
|
|
520
|
+
`[ObjectId][Convert] Provided value is not a valid 24-character hex string.`
|
|
521
|
+
);
|
|
522
|
+
throw new BadRequestError(
|
|
523
|
+
"Invalid ObjectId: must be a 24-character hexadecimal string."
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
return new import_mongodb2.ObjectId(id);
|
|
528
|
+
} catch (err) {
|
|
529
|
+
logger.error(
|
|
530
|
+
`[ObjectId][Convert] Failed to convert value to ObjectId: ${err instanceof Error ? err.message : err}`
|
|
531
|
+
);
|
|
532
|
+
throw new BadRequestError("Failed to convert value into a valid ObjectId.");
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/utils/paginate.ts
|
|
537
|
+
function paginate(items, page = 0, limit = 10, total) {
|
|
538
|
+
if (total === 0) {
|
|
539
|
+
return {
|
|
540
|
+
items: [],
|
|
541
|
+
pages: 0,
|
|
542
|
+
pageRange: "0-0 of 0"
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const startIndex = page * limit + 1;
|
|
546
|
+
const endIndex = Math.min(startIndex + items.length - 1, total);
|
|
547
|
+
return {
|
|
548
|
+
items,
|
|
549
|
+
pages: Math.ceil(total / limit),
|
|
550
|
+
pageRange: `${startIndex}-${endIndex} of ${total}`
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/utils/password.ts
|
|
555
|
+
var import_bcrypt = __toESM(require("bcrypt"));
|
|
556
|
+
var DEFAULT_SALT_ROUNDS = 10;
|
|
557
|
+
function comparePasswords(password, hashed) {
|
|
558
|
+
return __async(this, null, function* () {
|
|
559
|
+
try {
|
|
560
|
+
return yield import_bcrypt.default.compare(password, hashed);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
logger.error(
|
|
563
|
+
`[Password][Compare] Failed to compare passwords: ${error instanceof Error ? error.message : error}`
|
|
564
|
+
);
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function hashPassword(_0) {
|
|
570
|
+
return __async(this, arguments, function* (password, saltRounds = DEFAULT_SALT_ROUNDS) {
|
|
571
|
+
try {
|
|
572
|
+
return yield import_bcrypt.default.hash(password, saltRounds);
|
|
573
|
+
} catch (error) {
|
|
574
|
+
logger.error(
|
|
575
|
+
`[Password][Hash] Failed to hash password: ${error instanceof Error ? error.message : error}`
|
|
576
|
+
);
|
|
577
|
+
throw new InternalServerError("Failed to hash password.");
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/utils/redis.ts
|
|
583
|
+
var import_redis = require("redis");
|
|
584
|
+
|
|
585
|
+
// src/config.ts
|
|
586
|
+
var dotenv = __toESM(require("dotenv"));
|
|
587
|
+
dotenv.config();
|
|
588
|
+
function getEnv(key, fallback) {
|
|
589
|
+
const value = process.env[key];
|
|
590
|
+
if (value !== void 0) return value;
|
|
591
|
+
if (fallback !== void 0) return fallback;
|
|
592
|
+
throw new Error(`Missing required environment variable: ${key}`);
|
|
593
|
+
}
|
|
594
|
+
function getEnvNumber(key, fallback) {
|
|
595
|
+
const value = process.env[key];
|
|
596
|
+
if (value !== void 0) return Number(value);
|
|
597
|
+
if (fallback !== void 0) return fallback;
|
|
598
|
+
throw new Error(`Missing required environment variable: ${key}`);
|
|
599
|
+
}
|
|
600
|
+
function getEnvBoolean(key, fallback) {
|
|
601
|
+
const value = process.env[key];
|
|
602
|
+
if (value !== void 0) return value === "true";
|
|
603
|
+
if (fallback !== void 0) return fallback;
|
|
604
|
+
throw new Error(`Missing required environment variable: ${key}`);
|
|
605
|
+
}
|
|
606
|
+
var isDev = process.env.NODE_ENV !== "production";
|
|
607
|
+
var PORT = getEnvNumber("PORT", 3001);
|
|
608
|
+
var MONGO_URI = getEnv("MONGO_URI", "mongodb://localhost:27017");
|
|
609
|
+
var MONGO_DB = getEnv("MONGO_DB", "default");
|
|
610
|
+
var MONGO_DB_DEV = getEnv("MONGO_DB_DEV", `${MONGO_DB}-dev`);
|
|
611
|
+
var REDIS_HOST = getEnv("REDIS_HOST");
|
|
612
|
+
var REDIS_PORT = getEnvNumber("REDIS_PORT", 6379);
|
|
613
|
+
var REDIS_PASSWORD = getEnv("REDIS_PASSWORD");
|
|
614
|
+
var REDIS_TLS = getEnvBoolean("REDIS_TLS", true);
|
|
615
|
+
var MAILER_TRANSPORT_HOST = getEnv("MAILER_TRANSPORT_HOST");
|
|
616
|
+
var MAILER_TRANSPORT_PORT = getEnvNumber("MAILER_TRANSPORT_PORT", 465);
|
|
617
|
+
var MAILER_TRANSPORT_SECURE = getEnvBoolean(
|
|
618
|
+
"MAILER_TRANSPORT_SECURE",
|
|
619
|
+
true
|
|
620
|
+
);
|
|
621
|
+
var MAILER_EMAIL = getEnv("MAILER_EMAIL");
|
|
622
|
+
var MAILER_PASSWORD = getEnv("MAILER_PASSWORD");
|
|
623
|
+
var SECRET_KEY = getEnv("SECRET_KEY");
|
|
624
|
+
var ACCESS_TOKEN_SECRET = getEnv("ACCESS_TOKEN_SECRET");
|
|
625
|
+
var REFRESH_TOKEN_SECRET = getEnv("REFRESH_TOKEN_SECRET");
|
|
626
|
+
var ACCESS_TOKEN_EXPIRY = getEnv("ACCESS_TOKEN_EXPIRY", "15s");
|
|
627
|
+
var REFRESH_TOKEN_EXPIRY = getEnv("REFRESH_TOKEN_EXPIRY", "30d");
|
|
628
|
+
var OTP_FORGET_PASSWORD_EXPIRY = getEnv(
|
|
629
|
+
"OTP_FORGET_PASSWORD_EXPIRY",
|
|
630
|
+
"10m"
|
|
631
|
+
);
|
|
632
|
+
var OTP_USER_INVITE_EXPIRY = getEnv("OTP_USER_INVITE_EXPIRY", "3d");
|
|
633
|
+
var APP_COUNT = getEnv("APP_ACCOUNT", "http:localhost:3000");
|
|
634
|
+
|
|
635
|
+
// src/utils/redis.ts
|
|
636
|
+
var redisClient2 = null;
|
|
637
|
+
function initRedisClient() {
|
|
638
|
+
return __async(this, null, function* () {
|
|
639
|
+
if (redisClient2) {
|
|
640
|
+
logger.info("[Redis][Init] Redis connection is already established.");
|
|
641
|
+
return redisClient2;
|
|
642
|
+
}
|
|
643
|
+
redisClient2 = (0, import_redis.createClient)({
|
|
644
|
+
password: REDIS_PASSWORD,
|
|
645
|
+
socket: {
|
|
646
|
+
host: REDIS_HOST,
|
|
647
|
+
port: REDIS_PORT
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
function useRedisClient() {
|
|
653
|
+
if (!redisClient2) {
|
|
654
|
+
logger.error(
|
|
655
|
+
"[Redis][GetClient] Redis connection has not been initialized."
|
|
656
|
+
);
|
|
657
|
+
throw new Error(
|
|
658
|
+
"[Redis][GetClient] Redis connection is not initialized. Call initRedisClient() first."
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
return redisClient2;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/utils/s3.ts
|
|
665
|
+
var import_client_s3 = require("@aws-sdk/client-s3");
|
|
666
|
+
var import_stream = require("stream");
|
|
667
|
+
var useS3 = class {
|
|
668
|
+
constructor(config2) {
|
|
669
|
+
this.config = config2;
|
|
670
|
+
this.client = new import_client_s3.S3Client({
|
|
671
|
+
endpoint: config2.endpoint,
|
|
672
|
+
region: config2.region,
|
|
673
|
+
credentials: {
|
|
674
|
+
accessKeyId: config2.accessKeyId,
|
|
675
|
+
secretAccessKey: config2.secretAccessKey
|
|
676
|
+
},
|
|
677
|
+
forcePathStyle: config2.forcePathStyle
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
uploadObject(_0) {
|
|
681
|
+
return __async(this, arguments, function* ({
|
|
682
|
+
key,
|
|
683
|
+
body,
|
|
684
|
+
metadata = {},
|
|
685
|
+
contentType
|
|
686
|
+
}) {
|
|
687
|
+
try {
|
|
688
|
+
yield this.client.send(
|
|
689
|
+
new import_client_s3.PutObjectCommand({
|
|
690
|
+
Bucket: this.config.bucket,
|
|
691
|
+
Key: key,
|
|
692
|
+
Body: typeof body === "string" || Buffer.isBuffer(body) ? body : import_stream.Readable.from(body),
|
|
693
|
+
ACL: "public-read",
|
|
694
|
+
Metadata: metadata,
|
|
695
|
+
ContentType: contentType,
|
|
696
|
+
ContentLength: typeof body === "string" ? Buffer.byteLength(body) : body.length
|
|
697
|
+
})
|
|
698
|
+
);
|
|
699
|
+
logger.info(
|
|
700
|
+
`[S3][Upload] Uploaded "${key}" to bucket "${this.config.bucket}".`
|
|
701
|
+
);
|
|
702
|
+
return "Successfully uploaded file.";
|
|
703
|
+
} catch (error) {
|
|
704
|
+
logger.error(
|
|
705
|
+
`[S3][Upload][Error] Failed to upload "${key}" to bucket "${this.config.bucket}": ${error instanceof Error ? error.message : error}`
|
|
706
|
+
);
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
deleteObject(key) {
|
|
712
|
+
return __async(this, null, function* () {
|
|
713
|
+
try {
|
|
714
|
+
yield this.client.send(
|
|
715
|
+
new import_client_s3.DeleteObjectCommand({ Key: key, Bucket: this.config.bucket })
|
|
716
|
+
);
|
|
717
|
+
logger.info(
|
|
718
|
+
`[S3][Delete] Deleted "${key}" from bucket "${this.config.bucket}".`
|
|
719
|
+
);
|
|
720
|
+
return "Successfully deleted file.";
|
|
721
|
+
} catch (error) {
|
|
722
|
+
logger.error(
|
|
723
|
+
`[S3][Delete][Error] Failed to delete "${key}" from bucket "${this.config.bucket}": ${error instanceof Error ? error.message : error}`
|
|
724
|
+
);
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
731
|
+
0 && (module.exports = {
|
|
732
|
+
BadRequestError,
|
|
733
|
+
ForbiddenError,
|
|
734
|
+
HttpError,
|
|
735
|
+
InternalServerError,
|
|
736
|
+
NotFoundError,
|
|
737
|
+
UnauthorizedError,
|
|
738
|
+
UnprocessableEntityError,
|
|
739
|
+
authenticateJWT,
|
|
740
|
+
buildCacheKey,
|
|
741
|
+
comparePasswords,
|
|
742
|
+
errorHandler,
|
|
743
|
+
getTemplatePath,
|
|
744
|
+
hashPassword,
|
|
745
|
+
initRedisClient,
|
|
746
|
+
logger,
|
|
747
|
+
paginate,
|
|
748
|
+
renderHandlebarsTemplate,
|
|
749
|
+
signJwtToken,
|
|
750
|
+
toObjectId,
|
|
751
|
+
useAtlas,
|
|
752
|
+
useCache,
|
|
753
|
+
useMailer,
|
|
754
|
+
useRedis,
|
|
755
|
+
useRedisClient,
|
|
756
|
+
useS3
|
|
757
|
+
});
|
|
758
|
+
//# sourceMappingURL=index.js.map
|