@codisolutions23/node-utils 3.0.1 → 3.0.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/CHANGELOG.md +11 -0
- package/dist/index.d.mts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +226 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +230 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @codisolutions23/node-utils
|
|
2
2
|
|
|
3
|
+
## 3.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c740901: **Patch Changes and Improvements**
|
|
8
|
+
|
|
9
|
+
- **Dependencies**: Upgraded @aws-sdk and @smithy packages from v3.883.0 to v3.890.0, bringing latest features, bug fixes, and security improvements
|
|
10
|
+
- **Template Resolution**: Enhanced getTemplatePath to check multiple locations (src/public, public, provided directory) with fallback to src/public for improved flexibility
|
|
11
|
+
- **Handlebars Helpers**: Added formatCurrency for Philippine peso formatting and formatDate for flexible date formatting
|
|
12
|
+
- **Authentication Middleware**: Refactored for better modularity, added admin and role-based access control, improved logging and error handling with token revocation checks
|
|
13
|
+
|
|
3
14
|
## 3.0.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -6,14 +6,26 @@ import Redis from 'ioredis';
|
|
|
6
6
|
import * as winston from 'winston';
|
|
7
7
|
import { RedisClientType } from 'redis';
|
|
8
8
|
|
|
9
|
+
interface DecodedToken extends JwtPayload {
|
|
10
|
+
user?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
jti?: string;
|
|
13
|
+
role?: string;
|
|
14
|
+
}
|
|
9
15
|
interface AuthenticatedRequest extends Request {
|
|
10
|
-
user?:
|
|
11
|
-
|
|
12
|
-
id?: string;
|
|
16
|
+
user?: DecodedToken & {
|
|
17
|
+
id: string;
|
|
13
18
|
};
|
|
14
19
|
token?: string;
|
|
15
20
|
}
|
|
21
|
+
interface AuthOptions {
|
|
22
|
+
secretKey?: string;
|
|
23
|
+
isTokenRevoked?: (jti: string) => Promise<boolean>;
|
|
24
|
+
requiredRole?: string;
|
|
25
|
+
}
|
|
16
26
|
declare function authenticate(secretKey?: string, isTokenRevoked?: (jti: string) => Promise<boolean>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
27
|
+
declare function requireAdmin(secretKey?: string, isTokenRevoked?: (jti: string) => Promise<boolean>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
28
|
+
declare function requireRole(role: string, options?: Omit<AuthOptions, "requiredRole">): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
17
29
|
|
|
18
30
|
declare class HttpError extends Error {
|
|
19
31
|
readonly statusCode: number;
|
|
@@ -154,4 +166,4 @@ declare class useS3 {
|
|
|
154
166
|
|
|
155
167
|
declare function hashToken(token: string): string;
|
|
156
168
|
|
|
157
|
-
export { type AuthenticatedRequest, BadRequestError, ConflictError, ForbiddenError, HttpError, InternalServerError, NotFoundError, UnauthorizedError, UnprocessableEntityError, authenticate, buildCacheKey, comparePasswords, errorHandler, getTemplatePath, hashPassword, hashToken, initRedisClient, logger, paginate, renderHandlebarsTemplate, signJwtToken, toObjectId, useAtlas, useCache, useMailer, useRedis, useRedisClient, useS3 };
|
|
169
|
+
export { type AuthenticatedRequest, BadRequestError, ConflictError, ForbiddenError, HttpError, InternalServerError, NotFoundError, UnauthorizedError, UnprocessableEntityError, authenticate, buildCacheKey, comparePasswords, errorHandler, getTemplatePath, hashPassword, hashToken, initRedisClient, logger, paginate, renderHandlebarsTemplate, requireAdmin, requireRole, signJwtToken, toObjectId, useAtlas, useCache, useMailer, useRedis, useRedisClient, useS3 };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,14 +6,26 @@ import Redis from 'ioredis';
|
|
|
6
6
|
import * as winston from 'winston';
|
|
7
7
|
import { RedisClientType } from 'redis';
|
|
8
8
|
|
|
9
|
+
interface DecodedToken extends JwtPayload {
|
|
10
|
+
user?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
jti?: string;
|
|
13
|
+
role?: string;
|
|
14
|
+
}
|
|
9
15
|
interface AuthenticatedRequest extends Request {
|
|
10
|
-
user?:
|
|
11
|
-
|
|
12
|
-
id?: string;
|
|
16
|
+
user?: DecodedToken & {
|
|
17
|
+
id: string;
|
|
13
18
|
};
|
|
14
19
|
token?: string;
|
|
15
20
|
}
|
|
21
|
+
interface AuthOptions {
|
|
22
|
+
secretKey?: string;
|
|
23
|
+
isTokenRevoked?: (jti: string) => Promise<boolean>;
|
|
24
|
+
requiredRole?: string;
|
|
25
|
+
}
|
|
16
26
|
declare function authenticate(secretKey?: string, isTokenRevoked?: (jti: string) => Promise<boolean>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
27
|
+
declare function requireAdmin(secretKey?: string, isTokenRevoked?: (jti: string) => Promise<boolean>): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
28
|
+
declare function requireRole(role: string, options?: Omit<AuthOptions, "requiredRole">): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
17
29
|
|
|
18
30
|
declare class HttpError extends Error {
|
|
19
31
|
readonly statusCode: number;
|
|
@@ -154,4 +166,4 @@ declare class useS3 {
|
|
|
154
166
|
|
|
155
167
|
declare function hashToken(token: string): string;
|
|
156
168
|
|
|
157
|
-
export { type AuthenticatedRequest, BadRequestError, ConflictError, ForbiddenError, HttpError, InternalServerError, NotFoundError, UnauthorizedError, UnprocessableEntityError, authenticate, buildCacheKey, comparePasswords, errorHandler, getTemplatePath, hashPassword, hashToken, initRedisClient, logger, paginate, renderHandlebarsTemplate, signJwtToken, toObjectId, useAtlas, useCache, useMailer, useRedis, useRedisClient, useS3 };
|
|
169
|
+
export { type AuthenticatedRequest, BadRequestError, ConflictError, ForbiddenError, HttpError, InternalServerError, NotFoundError, UnauthorizedError, UnprocessableEntityError, authenticate, buildCacheKey, comparePasswords, errorHandler, getTemplatePath, hashPassword, hashToken, initRedisClient, logger, paginate, renderHandlebarsTemplate, requireAdmin, requireRole, signJwtToken, toObjectId, useAtlas, useCache, useMailer, useRedis, useRedisClient, useS3 };
|
package/dist/index.js
CHANGED
|
@@ -86,6 +86,8 @@ __export(index_exports, {
|
|
|
86
86
|
logger: () => logger,
|
|
87
87
|
paginate: () => paginate,
|
|
88
88
|
renderHandlebarsTemplate: () => renderHandlebarsTemplate,
|
|
89
|
+
requireAdmin: () => requireAdmin,
|
|
90
|
+
requireRole: () => requireRole,
|
|
89
91
|
signJwtToken: () => signJwtToken,
|
|
90
92
|
toObjectId: () => toObjectId,
|
|
91
93
|
useAtlas: () => useAtlas,
|
|
@@ -164,40 +166,191 @@ var InternalServerError = class extends HttpError {
|
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
// src/middleware/auth.middleware.ts
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
function extractToken(authHeader) {
|
|
170
|
+
logger.debug("Attempting to extract token from Authorization header");
|
|
171
|
+
if (!authHeader) {
|
|
172
|
+
logger.debug("No Authorization header provided");
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
if (!authHeader.startsWith("Bearer ")) {
|
|
176
|
+
logger.warn("Authorization header does not start with 'Bearer '", {
|
|
177
|
+
authHeader: authHeader.substring(0, 20) + "..."
|
|
178
|
+
});
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const token = authHeader.split(" ")[1] || null;
|
|
182
|
+
logger.debug("Token extraction result", {
|
|
183
|
+
hasToken: !!token,
|
|
184
|
+
tokenLength: token == null ? void 0 : token.length
|
|
185
|
+
});
|
|
186
|
+
return token;
|
|
187
|
+
}
|
|
188
|
+
function authenticateToken(req, options) {
|
|
189
|
+
return __async(this, null, function* () {
|
|
190
|
+
const {
|
|
191
|
+
secretKey = process.env.ACCESS_TOKEN_SECRET || "",
|
|
192
|
+
isTokenRevoked,
|
|
193
|
+
requiredRole
|
|
194
|
+
} = options;
|
|
195
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
196
|
+
logger.info("Starting authentication process", {
|
|
197
|
+
requestId,
|
|
198
|
+
path: req.path,
|
|
199
|
+
method: req.method,
|
|
200
|
+
requiredRole,
|
|
201
|
+
hasRevocationCheck: !!isTokenRevoked
|
|
202
|
+
});
|
|
203
|
+
const token = extractToken(req.headers.authorization);
|
|
171
204
|
if (!token) {
|
|
172
|
-
logger.error("No access token provided
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
);
|
|
205
|
+
logger.error("Authentication failed: No access token provided", {
|
|
206
|
+
requestId,
|
|
207
|
+
path: req.path
|
|
208
|
+
});
|
|
209
|
+
throw new UnauthorizedError("Access token is required to proceed.");
|
|
176
210
|
}
|
|
211
|
+
logger.debug("Token extracted successfully, proceeding with verification", {
|
|
212
|
+
requestId,
|
|
213
|
+
tokenLength: token.length
|
|
214
|
+
});
|
|
215
|
+
let decoded;
|
|
177
216
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
217
|
+
logger.debug("Verifying JWT token", { requestId });
|
|
218
|
+
decoded = import_jsonwebtoken.default.verify(token, secretKey);
|
|
219
|
+
logger.info("JWT token verified successfully", {
|
|
220
|
+
requestId,
|
|
221
|
+
userId: decoded.user || decoded.id,
|
|
222
|
+
role: decoded.role,
|
|
223
|
+
jti: decoded.jti,
|
|
224
|
+
exp: decoded.exp ? new Date(decoded.exp * 1e3).toISOString() : "unknown"
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error("JWT verification failed", {
|
|
228
|
+
requestId,
|
|
229
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
230
|
+
tokenLength: token.length
|
|
231
|
+
});
|
|
232
|
+
throw new UnauthorizedError(
|
|
233
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (isTokenRevoked && decoded.jti) {
|
|
237
|
+
logger.debug("Checking token revocation status", {
|
|
238
|
+
requestId,
|
|
239
|
+
jti: decoded.jti
|
|
240
|
+
});
|
|
241
|
+
const isRevoked = yield isTokenRevoked(decoded.jti);
|
|
242
|
+
if (isRevoked) {
|
|
243
|
+
logger.warn("Authentication failed: Token is revoked", {
|
|
244
|
+
requestId,
|
|
245
|
+
jti: decoded.jti,
|
|
246
|
+
userId: decoded.user || decoded.id
|
|
182
247
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"Your session has expired or the token is invalid. Please log in again."
|
|
186
|
-
)
|
|
248
|
+
throw new UnauthorizedError(
|
|
249
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
187
250
|
);
|
|
188
251
|
}
|
|
189
|
-
|
|
190
|
-
|
|
252
|
+
logger.debug("Token revocation check passed", {
|
|
253
|
+
requestId,
|
|
254
|
+
jti: decoded.jti
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (requiredRole && decoded.role !== requiredRole) {
|
|
258
|
+
logger.warn("Authorization failed: Insufficient permissions", {
|
|
259
|
+
requestId,
|
|
260
|
+
userId: decoded.user || decoded.id,
|
|
261
|
+
userRole: decoded.role,
|
|
262
|
+
requiredRole,
|
|
263
|
+
path: req.path
|
|
264
|
+
});
|
|
265
|
+
throw new ForbiddenError(
|
|
266
|
+
"Insufficient permissions to access this resource."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
req.user = __spreadProps(__spreadValues({}, decoded), {
|
|
270
|
+
id: decoded.user || decoded.id || ""
|
|
271
|
+
});
|
|
272
|
+
req.token = token;
|
|
273
|
+
logger.info("Authentication completed successfully", {
|
|
274
|
+
requestId,
|
|
275
|
+
userId: req.user.id,
|
|
276
|
+
role: req.user.role,
|
|
277
|
+
path: req.path,
|
|
278
|
+
method: req.method
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function authenticate(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
283
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
284
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
285
|
+
logger.debug("Authenticate middleware invoked", {
|
|
286
|
+
requestId,
|
|
287
|
+
path: req.path
|
|
288
|
+
});
|
|
289
|
+
try {
|
|
290
|
+
yield authenticateToken(req, { secretKey, isTokenRevoked });
|
|
291
|
+
logger.debug("Authenticate middleware completed successfully", {
|
|
292
|
+
requestId
|
|
191
293
|
});
|
|
192
|
-
req.token = token;
|
|
193
294
|
next();
|
|
194
295
|
} catch (error) {
|
|
195
|
-
logger.error("
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
296
|
+
logger.error("Authenticate middleware failed", {
|
|
297
|
+
requestId,
|
|
298
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
299
|
+
});
|
|
300
|
+
next(error);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function requireAdmin(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
305
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
306
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
307
|
+
logger.debug("RequireAdmin middleware invoked", {
|
|
308
|
+
requestId,
|
|
309
|
+
path: req.path
|
|
310
|
+
});
|
|
311
|
+
try {
|
|
312
|
+
yield authenticateToken(req, {
|
|
313
|
+
secretKey,
|
|
314
|
+
isTokenRevoked,
|
|
315
|
+
requiredRole: "admin"
|
|
316
|
+
});
|
|
317
|
+
logger.debug("RequireAdmin middleware completed successfully", {
|
|
318
|
+
requestId
|
|
319
|
+
});
|
|
320
|
+
next();
|
|
321
|
+
} catch (error) {
|
|
322
|
+
logger.error("RequireAdmin middleware failed", {
|
|
323
|
+
requestId,
|
|
324
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
325
|
+
});
|
|
326
|
+
next(error);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function requireRole(role, options) {
|
|
331
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
332
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
333
|
+
logger.debug("RequireRole middleware invoked", {
|
|
334
|
+
requestId,
|
|
335
|
+
path: req.path,
|
|
336
|
+
requiredRole: role
|
|
337
|
+
});
|
|
338
|
+
try {
|
|
339
|
+
yield authenticateToken(req, __spreadProps(__spreadValues({}, options), {
|
|
340
|
+
requiredRole: role
|
|
341
|
+
}));
|
|
342
|
+
logger.debug("RequireRole middleware completed successfully", {
|
|
343
|
+
requestId,
|
|
344
|
+
role
|
|
345
|
+
});
|
|
346
|
+
next();
|
|
347
|
+
} catch (error) {
|
|
348
|
+
logger.error("RequireRole middleware failed", {
|
|
349
|
+
requestId,
|
|
350
|
+
requiredRole: role,
|
|
351
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
352
|
+
});
|
|
353
|
+
next(error);
|
|
201
354
|
}
|
|
202
355
|
});
|
|
203
356
|
}
|
|
@@ -434,13 +587,58 @@ var import_path = __toESM(require("path"));
|
|
|
434
587
|
function getTemplatePath(directory, filePath) {
|
|
435
588
|
const ext = ".hbs";
|
|
436
589
|
const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;
|
|
437
|
-
|
|
590
|
+
const possiblePaths = [
|
|
591
|
+
import_path.default.join(process.cwd(), "src", "public", directory, file),
|
|
592
|
+
import_path.default.join(process.cwd(), "public", directory, file),
|
|
593
|
+
import_path.default.join(process.cwd(), directory, file)
|
|
594
|
+
];
|
|
595
|
+
const fs2 = require("fs");
|
|
596
|
+
for (const templatePath of possiblePaths) {
|
|
597
|
+
if (fs2.existsSync(templatePath)) {
|
|
598
|
+
return templatePath;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return possiblePaths[0];
|
|
438
602
|
}
|
|
439
603
|
|
|
440
604
|
// src/utils/handlebars-compiler.ts
|
|
441
605
|
var import_handlebars = __toESM(require("handlebars"));
|
|
442
606
|
var import_fs = require("fs");
|
|
443
607
|
var handlebarsTemplateCache = /* @__PURE__ */ new Map();
|
|
608
|
+
import_handlebars.default.registerHelper("formatCurrency", function(amount) {
|
|
609
|
+
if (typeof amount !== "number") return "0.00";
|
|
610
|
+
return new Intl.NumberFormat("en-PH", {
|
|
611
|
+
style: "decimal",
|
|
612
|
+
minimumFractionDigits: 2,
|
|
613
|
+
maximumFractionDigits: 2
|
|
614
|
+
}).format(amount);
|
|
615
|
+
});
|
|
616
|
+
import_handlebars.default.registerHelper(
|
|
617
|
+
"formatDate",
|
|
618
|
+
function(date, format2) {
|
|
619
|
+
if (!date) return "N/A";
|
|
620
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
621
|
+
if (isNaN(dateObj.getTime())) return "N/A";
|
|
622
|
+
const options = {};
|
|
623
|
+
if (format2.includes("MMMM")) options.month = "long";
|
|
624
|
+
else if (format2.includes("MMM")) options.month = "short";
|
|
625
|
+
else if (format2.includes("MM")) options.month = "2-digit";
|
|
626
|
+
if (format2.includes("D,")) options.day = "numeric";
|
|
627
|
+
else if (format2.includes("DD")) options.day = "2-digit";
|
|
628
|
+
if (format2.includes("YYYY")) options.year = "numeric";
|
|
629
|
+
else if (format2.includes("YY")) options.year = "2-digit";
|
|
630
|
+
if (format2.includes("h:mm A")) {
|
|
631
|
+
options.hour = "numeric";
|
|
632
|
+
options.minute = "2-digit";
|
|
633
|
+
options.hour12 = true;
|
|
634
|
+
} else if (format2.includes("HH:mm")) {
|
|
635
|
+
options.hour = "2-digit";
|
|
636
|
+
options.minute = "2-digit";
|
|
637
|
+
options.hour12 = false;
|
|
638
|
+
}
|
|
639
|
+
return new Intl.DateTimeFormat("en-US", options).format(dateObj);
|
|
640
|
+
}
|
|
641
|
+
);
|
|
444
642
|
function renderHandlebarsTemplate(_0) {
|
|
445
643
|
return __async(this, arguments, function* ({
|
|
446
644
|
context = {},
|
|
@@ -767,6 +965,8 @@ function hashToken(token) {
|
|
|
767
965
|
logger,
|
|
768
966
|
paginate,
|
|
769
967
|
renderHandlebarsTemplate,
|
|
968
|
+
requireAdmin,
|
|
969
|
+
requireRole,
|
|
770
970
|
signJwtToken,
|
|
771
971
|
toObjectId,
|
|
772
972
|
useAtlas,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/middleware/auth.middleware.ts","../src/utils/logger.ts","../src/utils/http-error.ts","../src/middleware/error-handler.middleware.ts","../src/utils/atlas.ts","../src/utils/ioredis.ts","../src/utils/cache.ts","../src/utils/cache-key.ts","../src/utils/get-template-path.ts","../src/utils/handlebars-compiler.ts","../src/utils/jwt.ts","../src/utils/mailer.ts","../src/utils/objectid-converter.ts","../src/utils/paginate.ts","../src/utils/password.ts","../src/utils/redis.ts","../src/config.ts","../src/utils/s3.ts","../src/utils/token.ts"],"sourcesContent":["export * from \"./middleware\";\r\nexport * from \"./utils\";\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport jwt, { JwtPayload } from \"jsonwebtoken\";\r\nimport { logger } from \"./../utils/logger\";\r\nimport { UnauthorizedError } from \"./../utils/http-error\";\r\n\r\nexport interface AuthenticatedRequest extends Request {\r\n user?: JwtPayload & { user?: string; id?: string };\r\n token?: string;\r\n}\r\n\r\nexport function authenticate(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const authHeader = req.headers.authorization;\r\n const token = authHeader?.split(\" \")[1];\r\n\r\n if (!token) {\r\n logger.error(\"No access token provided in the request.\");\r\n return next(\r\n new UnauthorizedError(\"Access token is required to proceed.\")\r\n );\r\n }\r\n\r\n try {\r\n const decoded = jwt.verify(token, secretKey) as JwtPayload & {\r\n user?: string;\r\n id?: string;\r\n jti?: string;\r\n };\r\n\r\n if (\r\n isTokenRevoked &&\r\n decoded.jti &&\r\n (await isTokenRevoked(decoded.jti))\r\n ) {\r\n logger.warn(\"Access token is revoked (blacklisted).\", {\r\n jti: decoded.jti,\r\n });\r\n return next(\r\n new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n )\r\n );\r\n }\r\n\r\n req.user = {\r\n ...(decoded as any),\r\n id: (decoded as any).user || (decoded as any).id,\r\n };\r\n req.token = token;\r\n next();\r\n } catch (error) {\r\n logger.error(\"Failed to verify access token.\", error);\r\n return next(\r\n new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n )\r\n );\r\n }\r\n };\r\n}\r\n","import * as winston from \"winston\";\r\n\r\nconst transports = [\r\n new winston.transports.File({ filename: \"error.log\", level: \"error\" }),\r\n new winston.transports.File({ filename: \"combined.log\" }),\r\n];\r\n\r\nexport const logger = winston.createLogger({\r\n level: \"info\",\r\n format: winston.format.combine(\r\n winston.format.timestamp(),\r\n winston.format.json()\r\n ),\r\n transports,\r\n});\r\n","export class HttpError extends Error {\r\n public readonly statusCode: number;\r\n public readonly isOperational: boolean;\r\n\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n isOperational: boolean = true\r\n ) {\r\n super(message);\r\n\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n\r\n this.statusCode = statusCode;\r\n this.isOperational = isOperational;\r\n\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, this.constructor);\r\n }\r\n }\r\n}\r\n\r\nexport class BadRequestError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be processed. Please review your input and try again.\"\r\n ) {\r\n super(message, 400, true);\r\n }\r\n}\r\n\r\nexport class UnauthorizedError extends HttpError {\r\n constructor(\r\n message: string = \"Authentication is required to access this resource.\"\r\n ) {\r\n super(message, 401, true);\r\n }\r\n}\r\n\r\nexport class ForbiddenError extends HttpError {\r\n constructor(\r\n message: string = \"You do not have the necessary permissions to perform this action.\"\r\n ) {\r\n super(message, 403, true);\r\n }\r\n}\r\n\r\nexport class NotFoundError extends HttpError {\r\n constructor(message: string = \"The requested resource could not be found.\") {\r\n super(message, 404, true);\r\n }\r\n}\r\n\r\nexport class ConflictError extends HttpError {\r\n constructor(\r\n message: string = \"A resource with the provided values already exists.\"\r\n ) {\r\n super(message, 409, true);\r\n }\r\n}\r\n\r\nexport class UnprocessableEntityError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be completed due to invalid or incomplete information.\"\r\n ) {\r\n super(message, 422, true);\r\n }\r\n}\r\n\r\nexport class InternalServerError extends HttpError {\r\n constructor(\r\n message: string = \"An internal server error occurred. Please try again later.\"\r\n ) {\r\n super(message, 500, true);\r\n }\r\n}\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport { HttpError, InternalServerError } from \"./../utils/http-error\";\r\nimport { logger } from \"./../utils/logger\";\r\n\r\nexport const errorHandler = (\r\n error: HttpError,\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n) => {\r\n if (error.isOperational) {\r\n res\r\n .status(error.statusCode)\r\n .json({ status: \"error\", message: error.message });\r\n } else {\r\n logger.error({ message: error.message });\r\n res\r\n .status(500)\r\n .json({ status: \"error\", message: new InternalServerError().message });\r\n }\r\n\r\n return;\r\n};\r\n","import { Db, MongoClient } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport {\r\n BadRequestError,\r\n InternalServerError,\r\n NotFoundError,\r\n} from \"./http-error\";\r\n\r\ninterface AtlasConfig {\r\n uri: string;\r\n db: string;\r\n name?: string;\r\n}\r\n\r\nexport class useAtlas {\r\n private static mongoClient: MongoClient | null = null;\r\n private static mongoDb: Db | null = null;\r\n\r\n public static async connect(config: AtlasConfig): Promise<void> {\r\n if (this.mongoClient) {\r\n logger.warn(\r\n \"[MongoDB][Connect] Client is already connected. Skipping new connection.\"\r\n );\r\n throw new BadRequestError(\"A MongoDB connection is already established.\");\r\n }\r\n\r\n const { db, uri } = config;\r\n this.mongoClient = new MongoClient(uri, {\r\n maxPoolSize: 10,\r\n maxIdleTimeMS: 60000,\r\n connectTimeoutMS: 60000,\r\n });\r\n\r\n try {\r\n await this.mongoClient.connect();\r\n this.mongoDb = this.mongoClient.db(db);\r\n\r\n logger.info(\r\n `[MongoDB][Connect] Connected to database \"${this.mongoDb.databaseName}\".`\r\n );\r\n } catch (error) {\r\n this.mongoClient = null;\r\n\r\n logger.error(\r\n `[MongoDB][Connect] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\r\n \"Failed to connect to the database. Please try again later.\"\r\n );\r\n }\r\n }\r\n\r\n public static getClient(): MongoClient | null {\r\n if (!this.mongoClient) {\r\n logger.warn(`[MongoDB][GetClient] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return this.mongoClient;\r\n }\r\n\r\n public static getDb(): Db | null {\r\n if (!this.mongoDb) {\r\n logger.warn(`[MongoDB][GetDb] Database instance is not available.`);\r\n throw new NotFoundError(\"MongoDB database instance is not available.\");\r\n }\r\n\r\n return this.mongoDb;\r\n }\r\n\r\n public static startSession(): import(\"mongodb\").ClientSession {\r\n const client = this.getClient();\r\n\r\n if (!client) {\r\n logger.warn(`[MongoDB][StartSession] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return client.startSession();\r\n }\r\n\r\n public static async close(): Promise<void> {\r\n if (this.mongoClient) {\r\n try {\r\n await this.mongoClient.close();\r\n\r\n logger.info(`[MongoDB][Close] Connection closed.`);\r\n } catch (error) {\r\n logger.error(\r\n `[MongoDB][Close] Error closing connection: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to close MongoDB connection.\");\r\n } finally {\r\n this.mongoClient = null;\r\n this.mongoDb = null;\r\n }\r\n } else {\r\n logger.warn(`[MongoDB][Close] No client to disconnect.`);\r\n throw new BadRequestError(\"No MongoDB connection exists to close.\");\r\n }\r\n }\r\n}\r\n","import Redis from \"ioredis\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nlet redisClient: Redis | null = null;\r\n\r\ntype RedisOptions = {\r\n host?: string;\r\n port?: number;\r\n username?: string;\r\n password?: string;\r\n};\r\n\r\nexport function useRedis() {\r\n function initialize(options: RedisOptions) {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n options.host = options.host ?? \"localhost\";\r\n options.port = options.port ?? 6379;\r\n options.username = options.username ?? \"default\";\r\n options.password = options.password ?? \"\";\r\n\r\n redisClient = new Redis({\r\n host: options.host,\r\n port: options.port,\r\n ...(options.username && { username: options.username }),\r\n ...(options.password && { password: options.password }),\r\n });\r\n\r\n redisClient.on(\"connect\", () => {\r\n logger.info(\r\n `[Redis][Connect] Redis client connected at ${options.host}:${options.port}.`\r\n );\r\n });\r\n\r\n redisClient.on(\"error\", (error) => {\r\n logger.error(\r\n `[Redis][Error] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n });\r\n\r\n return redisClient;\r\n }\r\n\r\n function getClient(): Redis {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new BadRequestError(\r\n \"Redis connection is not initialized. Please call initialize() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n }\r\n\r\n function disconnect() {\r\n if (redisClient) {\r\n redisClient.quit();\r\n logger.info(\"[Redis][Disconnect] Redis connection has been closed.\");\r\n redisClient = null;\r\n } else {\r\n logger.warn(\"[Redis][Disconnect] No Redis client to disconnect.\");\r\n }\r\n }\r\n\r\n return {\r\n initialize,\r\n getClient,\r\n disconnect,\r\n };\r\n}\r\n","import { useRedis } from \"./ioredis\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_TTL = 300;\r\n\r\nexport function useCache() {\r\n function getRedisClient() {\r\n return useRedis().getClient();\r\n }\r\n\r\n async function get<T = unknown>(cacheKey: string): Promise<T | null> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const value = await redisClient.get(cacheKey);\r\n\r\n return value ? (JSON.parse(value) as T) : null;\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Get] Failed to retrieve key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n\r\n return null;\r\n }\r\n }\r\n\r\n async function set<T = unknown>(\r\n cacheKey: string,\r\n data: T,\r\n ttl: number = DEFAULT_TTL,\r\n group?: string\r\n ): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.set(cacheKey, JSON.stringify(data), \"EX\", ttl);\r\n logger.info(`[Cache][Set] Stored key \"${cacheKey}\" with TTL ${ttl}s.`);\r\n\r\n if (group) await redisClient.sadd(`cache:group:${group}`, cacheKey);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Set] Failed to store key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function remove(cacheKey: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.del(cacheKey);\r\n logger.info(`[Cache][Remove] Key \"${cacheKey}\" has been deleted.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Remove] Failed to delete key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function clearGroup(group: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const keys = await redisClient.smembers(`cache:group:${group}`);\r\n if (keys.length) await redisClient.del(...keys);\r\n\r\n await redisClient.del(`cache:group:${group}`);\r\n logger.info(`[Cache][ClearGroup] Cleared group \"${group}\" and its keys.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][ClearGroup] Failed to clear group \"${group}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n return {\r\n get,\r\n set,\r\n remove,\r\n clearGroup,\r\n };\r\n}\r\n","export function buildCacheKey(\r\n prefix: string,\r\n values: Record<string, any>\r\n): string {\r\n const query = Object.entries(values)\r\n .sort(([a], [b]) => a.localeCompare(b))\r\n .map(\r\n ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`\r\n )\r\n .join(\"&\");\r\n\r\n return `${prefix}:${query}`;\r\n}\r\n","import path from \"path\";\r\n\r\nexport function getTemplatePath(directory: string, filePath: string) {\r\n const ext = \".hbs\";\r\n const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;\r\n return path.resolve(directory, file);\r\n}\r\n","import Handlebars from \"handlebars\";\r\nimport { promises as fs } from \"fs\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface CompileOptions {\r\n context?: Record<string, any>;\r\n filePath: string;\r\n}\r\n\r\nconst handlebarsTemplateCache = new Map<string, Handlebars.TemplateDelegate>();\r\n\r\nexport async function renderHandlebarsTemplate({\r\n context = {},\r\n filePath,\r\n}: CompileOptions): Promise<string> {\r\n try {\r\n let compiledTemplate = handlebarsTemplateCache.get(filePath);\r\n\r\n if (!compiledTemplate) {\r\n logger.info(\r\n `[Template][Compile] Compiling and caching template from \"${filePath}\".`\r\n );\r\n const fileContent = await fs.readFile(filePath, \"utf8\");\r\n compiledTemplate = Handlebars.compile(fileContent);\r\n handlebarsTemplateCache.set(filePath, compiledTemplate);\r\n } else {\r\n logger.info(`[Template][Cache] Using cached template for \"${filePath}\".`);\r\n }\r\n\r\n return compiledTemplate(context);\r\n } catch (error) {\r\n logger.error(\r\n `[Template][Render] Failed to render template \"${filePath}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to render Handlebars template.\");\r\n }\r\n}\r\n","import jwt from \"jsonwebtoken\";\r\nimport { BadRequestError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface JwtSignParams {\r\n payload?: Record<string, unknown>;\r\n secretKey: string;\r\n signOptions?: jwt.SignOptions;\r\n}\r\n\r\nexport function signJwtToken({\r\n payload = {},\r\n secretKey = \"\",\r\n signOptions = {},\r\n}: JwtSignParams): string {\r\n if (!secretKey) {\r\n logger.error(`[JWT][Sign] Secret key is missing. Cannot sign token.`);\r\n throw new BadRequestError(\"A JWT secret key must be provided.\");\r\n }\r\n\r\n try {\r\n logger.info(`[JWT][Sign] Signing JWT token.`);\r\n return jwt.sign(payload, secretKey, signOptions);\r\n } catch (error) {\r\n logger.error(\r\n `[JWT][Sign] Failed to sign JWT token: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw error;\r\n }\r\n}\r\n","import { createTransport, SendMailOptions, Transporter } from \"nodemailer\";\r\nimport { logger } from \"./logger\";\r\nimport { InternalServerError } from \"./http-error\";\r\n\r\ninterface MailerConfig {\r\n email: string;\r\n password: string;\r\n host: string;\r\n port: number;\r\n secure: boolean;\r\n}\r\n\r\nexport class useMailer {\r\n private transporter: Transporter;\r\n\r\n constructor(private config: MailerConfig) {\r\n this.transporter = createTransport({\r\n host: config.host,\r\n port: config.port,\r\n secure: config.secure,\r\n auth: { user: config.email, pass: config.password },\r\n });\r\n }\r\n\r\n async sendMail({\r\n sender,\r\n to,\r\n subject,\r\n text,\r\n html,\r\n }: {\r\n sender?: string;\r\n to: string;\r\n subject: string;\r\n text?: string;\r\n html?: string;\r\n }): Promise<string> {\r\n const from = sender\r\n ? `${sender} <${this.config.email}>`\r\n : this.config.email;\r\n\r\n const mailOptions: SendMailOptions = {\r\n from,\r\n to,\r\n subject,\r\n ...(text && { text }),\r\n ...(html && { html }),\r\n };\r\n\r\n try {\r\n await this.transporter.sendMail(mailOptions);\r\n logger.info(\r\n `[Mailer][Send] Email sent to \"${to}\" with subject \"${subject}\".`\r\n );\r\n\r\n return \"Mail sent successfully.\";\r\n } catch (error) {\r\n logger.error(\r\n `[Mailer][Send] Failed to send email to \"${to}\" with subject \"${subject}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to send email.\");\r\n }\r\n }\r\n}\r\n","import { ObjectId } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nexport function toObjectId(id: string | ObjectId): ObjectId {\r\n if (!id) {\r\n logger.error(\r\n `[ObjectId][Convert] No value provided for MongoDB ObjectId conversion.`\r\n );\r\n throw new BadRequestError(\r\n \"A value must be provided for ObjectId conversion.\"\r\n );\r\n }\r\n\r\n if (id instanceof ObjectId) return id;\r\n\r\n if (!/^[0-9a-fA-F]{24}$/.test(id)) {\r\n logger.error(\r\n `[ObjectId][Convert] Provided value is not a valid 24-character hex string.`\r\n );\r\n throw new BadRequestError(\r\n \"Invalid ObjectId: must be a 24-character hexadecimal string.\"\r\n );\r\n }\r\n\r\n try {\r\n return new ObjectId(id);\r\n } catch (err) {\r\n logger.error(\r\n `[ObjectId][Convert] Failed to convert value to ObjectId: ${\r\n err instanceof Error ? err.message : err\r\n }`\r\n );\r\n throw new BadRequestError(\"Failed to convert value into a valid ObjectId.\");\r\n }\r\n}\r\n","export function paginate<T>(\r\n items: T[],\r\n page: number = 0,\r\n limit: number = 10,\r\n total: number\r\n) {\r\n if (total === 0 || items.length === 0) {\r\n return {\r\n items: [],\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n\r\n const startIndex = (page - 1) * limit + 1;\r\n if (startIndex > total) {\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n const endIndex = Math.min(startIndex + items.length - 1, total);\r\n\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `${startIndex}-${endIndex} of ${total}`,\r\n };\r\n}\r\n","import bcrypt from \"bcrypt\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_SALT_ROUNDS = 10;\r\n\r\nexport async function comparePasswords(\r\n password: string,\r\n hashed: string\r\n): Promise<boolean> {\r\n try {\r\n return await bcrypt.compare(password, hashed);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Compare] Failed to compare passwords: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n return false;\r\n }\r\n}\r\n\r\nexport async function hashPassword(\r\n password: string,\r\n saltRounds: number = DEFAULT_SALT_ROUNDS\r\n): Promise<string> {\r\n try {\r\n return await bcrypt.hash(password, saltRounds);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Hash] Failed to hash password: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to hash password.\");\r\n }\r\n}\r\n","import { createClient, RedisClientType } from \"redis\";\r\nimport { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from \"./../config\";\r\nimport { logger } from \"./logger\";\r\n\r\nlet redisClient: RedisClientType | null = null;\r\n\r\nexport async function initRedisClient() {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n redisClient = createClient({\r\n password: REDIS_PASSWORD,\r\n socket: {\r\n host: REDIS_HOST,\r\n port: REDIS_PORT,\r\n },\r\n });\r\n}\r\n\r\nexport function useRedisClient(): RedisClientType {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new Error(\r\n \"[Redis][GetClient] Redis connection is not initialized. Call initRedisClient() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n}\r\n","import * as dotenv from \"dotenv\";\r\n\r\ndotenv.config();\r\n\r\nfunction getEnv(key: string, fallback?: string): string {\r\n const value = process.env[key];\r\n if (value !== undefined) return value as string;\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvNumber(key: string, fallback?: number): number {\r\n const value = process.env[key];\r\n if (value !== undefined) return Number(value);\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvBoolean(key: string, fallback?: boolean): boolean {\r\n const value = process.env[key];\r\n if (value !== undefined) return value === \"true\";\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\n// Redis\r\nexport const REDIS_HOST = getEnv(\"REDIS_HOST\");\r\nexport const REDIS_PORT = getEnvNumber(\"REDIS_PORT\", 6379);\r\nexport const REDIS_PASSWORD = getEnv(\"REDIS_PASSWORD\");\r\nexport const REDIS_TLS = getEnvBoolean(\"REDIS_TLS\", true);\r\n","import {\r\n DeleteObjectCommand,\r\n PutObjectCommand,\r\n S3Client,\r\n} from \"@aws-sdk/client-s3\";\r\nimport { Readable } from \"stream\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface S3Config {\r\n accessKeyId: string;\r\n secretAccessKey: string;\r\n endpoint: string;\r\n region: string;\r\n bucket: string;\r\n forcePathStyle: boolean;\r\n}\r\n\r\nexport class useS3 {\r\n private client: S3Client;\r\n\r\n constructor(private config: S3Config) {\r\n this.client = new S3Client({\r\n endpoint: config.endpoint,\r\n region: config.region,\r\n credentials: {\r\n accessKeyId: config.accessKeyId,\r\n secretAccessKey: config.secretAccessKey,\r\n },\r\n forcePathStyle: config.forcePathStyle,\r\n });\r\n }\r\n\r\n async uploadObject({\r\n key,\r\n body,\r\n metadata = {},\r\n contentType,\r\n }: {\r\n key: string;\r\n body: string | Buffer;\r\n metadata?: Record<string, string>;\r\n contentType?: string;\r\n }): Promise<string> {\r\n try {\r\n await this.client.send(\r\n new PutObjectCommand({\r\n Bucket: this.config.bucket,\r\n Key: key,\r\n Body:\r\n typeof body === \"string\" || Buffer.isBuffer(body)\r\n ? body\r\n : Readable.from(body),\r\n ACL: \"public-read\",\r\n Metadata: metadata,\r\n ContentType: contentType,\r\n ContentLength:\r\n typeof body === \"string\" ? Buffer.byteLength(body) : body.length,\r\n })\r\n );\r\n\r\n logger.info(\r\n `[S3][Upload] Uploaded \"${key}\" to bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully uploaded file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Upload][Error] Failed to upload \"${key}\" to bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n async deleteObject(key: string) {\r\n try {\r\n await this.client.send(\r\n new DeleteObjectCommand({ Key: key, Bucket: this.config.bucket })\r\n );\r\n\r\n logger.info(\r\n `[S3][Delete] Deleted \"${key}\" from bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully deleted file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Delete][Error] Failed to delete \"${key}\" from bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n}\r\n","import crypto from \"crypto\";\r\n\r\n// Deterministic hash for tokens at rest (do NOT use bcrypt here)\r\n// Optionally salt via env if provided\r\nconst TOKEN_HASH_SALT = process.env.REFRESH_TOKEN_HASH_SALT || \"\";\r\n\r\nexport function hashToken(token: string): string {\r\n return crypto\r\n .createHash(\"sha256\")\r\n .update(TOKEN_HASH_SALT + token)\r\n .digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":["transports","jwt","config","Redis","redisClient","path","fs","Handlebars","import_jsonwebtoken","jwt","config","import_mongodb","bcrypt","redisClient","config","crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/middleware/auth.middleware.ts","../src/utils/logger.ts","../src/utils/http-error.ts","../src/middleware/error-handler.middleware.ts","../src/utils/atlas.ts","../src/utils/ioredis.ts","../src/utils/cache.ts","../src/utils/cache-key.ts","../src/utils/get-template-path.ts","../src/utils/handlebars-compiler.ts","../src/utils/jwt.ts","../src/utils/mailer.ts","../src/utils/objectid-converter.ts","../src/utils/paginate.ts","../src/utils/password.ts","../src/utils/redis.ts","../src/config.ts","../src/utils/s3.ts","../src/utils/token.ts"],"sourcesContent":["export * from \"./middleware\";\r\nexport * from \"./utils\";\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport jwt, { JwtPayload } from \"jsonwebtoken\";\r\nimport { logger } from \"./../utils/logger\";\r\nimport { UnauthorizedError, ForbiddenError } from \"./../utils/http-error\";\r\n\r\ninterface DecodedToken extends JwtPayload {\r\n user?: string;\r\n id?: string;\r\n jti?: string;\r\n role?: string;\r\n}\r\n\r\nexport interface AuthenticatedRequest extends Request {\r\n user?: DecodedToken & { id: string };\r\n token?: string;\r\n}\r\n\r\ninterface AuthOptions {\r\n secretKey?: string;\r\n isTokenRevoked?: (jti: string) => Promise<boolean>;\r\n requiredRole?: string;\r\n}\r\n\r\n// Helper function to extract token from Authorization header\r\nfunction extractToken(authHeader?: string): string | null {\r\n logger.debug(\"Attempting to extract token from Authorization header\");\r\n\r\n if (!authHeader) {\r\n logger.debug(\"No Authorization header provided\");\r\n return null;\r\n }\r\n\r\n if (!authHeader.startsWith(\"Bearer \")) {\r\n logger.warn(\"Authorization header does not start with 'Bearer '\", {\r\n authHeader: authHeader.substring(0, 20) + \"...\",\r\n });\r\n return null;\r\n }\r\n\r\n const token = authHeader.split(\" \")[1] || null;\r\n logger.debug(\"Token extraction result\", {\r\n hasToken: !!token,\r\n tokenLength: token?.length,\r\n });\r\n return token;\r\n}\r\n\r\n// Core authentication logic - DRY principle\r\nasync function authenticateToken(\r\n req: AuthenticatedRequest,\r\n options: AuthOptions\r\n): Promise<void> {\r\n const {\r\n secretKey = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked,\r\n requiredRole,\r\n } = options;\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n\r\n logger.info(\"Starting authentication process\", {\r\n requestId,\r\n path: req.path,\r\n method: req.method,\r\n requiredRole,\r\n hasRevocationCheck: !!isTokenRevoked,\r\n });\r\n\r\n const token = extractToken(req.headers.authorization);\r\n if (!token) {\r\n logger.error(\"Authentication failed: No access token provided\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n throw new UnauthorizedError(\"Access token is required to proceed.\");\r\n }\r\n\r\n logger.debug(\"Token extracted successfully, proceeding with verification\", {\r\n requestId,\r\n tokenLength: token.length,\r\n });\r\n\r\n let decoded: DecodedToken;\r\n try {\r\n logger.debug(\"Verifying JWT token\", { requestId });\r\n decoded = jwt.verify(token, secretKey) as DecodedToken;\r\n logger.info(\"JWT token verified successfully\", {\r\n requestId,\r\n userId: decoded.user || decoded.id,\r\n role: decoded.role,\r\n jti: decoded.jti,\r\n exp: decoded.exp ? new Date(decoded.exp * 1000).toISOString() : \"unknown\",\r\n });\r\n } catch (error) {\r\n logger.error(\"JWT verification failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n tokenLength: token.length,\r\n });\r\n throw new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n );\r\n }\r\n\r\n // Check if token is revoked\r\n if (isTokenRevoked && decoded.jti) {\r\n logger.debug(\"Checking token revocation status\", {\r\n requestId,\r\n jti: decoded.jti,\r\n });\r\n const isRevoked = await isTokenRevoked(decoded.jti);\r\n if (isRevoked) {\r\n logger.warn(\"Authentication failed: Token is revoked\", {\r\n requestId,\r\n jti: decoded.jti,\r\n userId: decoded.user || decoded.id,\r\n });\r\n throw new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n );\r\n }\r\n logger.debug(\"Token revocation check passed\", {\r\n requestId,\r\n jti: decoded.jti,\r\n });\r\n }\r\n\r\n // Check role requirements\r\n if (requiredRole && decoded.role !== requiredRole) {\r\n logger.warn(\"Authorization failed: Insufficient permissions\", {\r\n requestId,\r\n userId: decoded.user || decoded.id,\r\n userRole: decoded.role,\r\n requiredRole,\r\n path: req.path,\r\n });\r\n throw new ForbiddenError(\r\n \"Insufficient permissions to access this resource.\"\r\n );\r\n }\r\n\r\n // Set user data on request\r\n req.user = {\r\n ...decoded,\r\n id: decoded.user || decoded.id || \"\",\r\n };\r\n req.token = token;\r\n\r\n logger.info(\"Authentication completed successfully\", {\r\n requestId,\r\n userId: req.user.id,\r\n role: req.user.role,\r\n path: req.path,\r\n method: req.method,\r\n });\r\n}\r\n\r\nexport function authenticate(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"Authenticate middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, { secretKey, isTokenRevoked });\r\n logger.debug(\"Authenticate middleware completed successfully\", {\r\n requestId,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"Authenticate middleware failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n\r\nexport function requireAdmin(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"RequireAdmin middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, {\r\n secretKey,\r\n isTokenRevoked,\r\n requiredRole: \"admin\",\r\n });\r\n logger.debug(\"RequireAdmin middleware completed successfully\", {\r\n requestId,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"RequireAdmin middleware failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n\r\n// Additional helper middleware for role-based access\r\nexport function requireRole(\r\n role: string,\r\n options?: Omit<AuthOptions, \"requiredRole\">\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"RequireRole middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n requiredRole: role,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, {\r\n ...options,\r\n requiredRole: role,\r\n });\r\n logger.debug(\"RequireRole middleware completed successfully\", {\r\n requestId,\r\n role,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"RequireRole middleware failed\", {\r\n requestId,\r\n requiredRole: role,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n","import * as winston from \"winston\";\r\n\r\nconst transports = [\r\n new winston.transports.File({ filename: \"error.log\", level: \"error\" }),\r\n new winston.transports.File({ filename: \"combined.log\" }),\r\n];\r\n\r\nexport const logger = winston.createLogger({\r\n level: \"info\",\r\n format: winston.format.combine(\r\n winston.format.timestamp(),\r\n winston.format.json()\r\n ),\r\n transports,\r\n});\r\n","export class HttpError extends Error {\r\n public readonly statusCode: number;\r\n public readonly isOperational: boolean;\r\n\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n isOperational: boolean = true\r\n ) {\r\n super(message);\r\n\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n\r\n this.statusCode = statusCode;\r\n this.isOperational = isOperational;\r\n\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, this.constructor);\r\n }\r\n }\r\n}\r\n\r\nexport class BadRequestError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be processed. Please review your input and try again.\"\r\n ) {\r\n super(message, 400, true);\r\n }\r\n}\r\n\r\nexport class UnauthorizedError extends HttpError {\r\n constructor(\r\n message: string = \"Authentication is required to access this resource.\"\r\n ) {\r\n super(message, 401, true);\r\n }\r\n}\r\n\r\nexport class ForbiddenError extends HttpError {\r\n constructor(\r\n message: string = \"You do not have the necessary permissions to perform this action.\"\r\n ) {\r\n super(message, 403, true);\r\n }\r\n}\r\n\r\nexport class NotFoundError extends HttpError {\r\n constructor(message: string = \"The requested resource could not be found.\") {\r\n super(message, 404, true);\r\n }\r\n}\r\n\r\nexport class ConflictError extends HttpError {\r\n constructor(\r\n message: string = \"A resource with the provided values already exists.\"\r\n ) {\r\n super(message, 409, true);\r\n }\r\n}\r\n\r\nexport class UnprocessableEntityError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be completed due to invalid or incomplete information.\"\r\n ) {\r\n super(message, 422, true);\r\n }\r\n}\r\n\r\nexport class InternalServerError extends HttpError {\r\n constructor(\r\n message: string = \"An internal server error occurred. Please try again later.\"\r\n ) {\r\n super(message, 500, true);\r\n }\r\n}\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport { HttpError, InternalServerError } from \"./../utils/http-error\";\r\nimport { logger } from \"./../utils/logger\";\r\n\r\nexport const errorHandler = (\r\n error: HttpError,\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n) => {\r\n if (error.isOperational) {\r\n res\r\n .status(error.statusCode)\r\n .json({ status: \"error\", message: error.message });\r\n } else {\r\n logger.error({ message: error.message });\r\n res\r\n .status(500)\r\n .json({ status: \"error\", message: new InternalServerError().message });\r\n }\r\n\r\n return;\r\n};\r\n","import { Db, MongoClient } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport {\r\n BadRequestError,\r\n InternalServerError,\r\n NotFoundError,\r\n} from \"./http-error\";\r\n\r\ninterface AtlasConfig {\r\n uri: string;\r\n db: string;\r\n name?: string;\r\n}\r\n\r\nexport class useAtlas {\r\n private static mongoClient: MongoClient | null = null;\r\n private static mongoDb: Db | null = null;\r\n\r\n public static async connect(config: AtlasConfig): Promise<void> {\r\n if (this.mongoClient) {\r\n logger.warn(\r\n \"[MongoDB][Connect] Client is already connected. Skipping new connection.\"\r\n );\r\n throw new BadRequestError(\"A MongoDB connection is already established.\");\r\n }\r\n\r\n const { db, uri } = config;\r\n this.mongoClient = new MongoClient(uri, {\r\n maxPoolSize: 10,\r\n maxIdleTimeMS: 60000,\r\n connectTimeoutMS: 60000,\r\n });\r\n\r\n try {\r\n await this.mongoClient.connect();\r\n this.mongoDb = this.mongoClient.db(db);\r\n\r\n logger.info(\r\n `[MongoDB][Connect] Connected to database \"${this.mongoDb.databaseName}\".`\r\n );\r\n } catch (error) {\r\n this.mongoClient = null;\r\n\r\n logger.error(\r\n `[MongoDB][Connect] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\r\n \"Failed to connect to the database. Please try again later.\"\r\n );\r\n }\r\n }\r\n\r\n public static getClient(): MongoClient | null {\r\n if (!this.mongoClient) {\r\n logger.warn(`[MongoDB][GetClient] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return this.mongoClient;\r\n }\r\n\r\n public static getDb(): Db | null {\r\n if (!this.mongoDb) {\r\n logger.warn(`[MongoDB][GetDb] Database instance is not available.`);\r\n throw new NotFoundError(\"MongoDB database instance is not available.\");\r\n }\r\n\r\n return this.mongoDb;\r\n }\r\n\r\n public static startSession(): import(\"mongodb\").ClientSession {\r\n const client = this.getClient();\r\n\r\n if (!client) {\r\n logger.warn(`[MongoDB][StartSession] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return client.startSession();\r\n }\r\n\r\n public static async close(): Promise<void> {\r\n if (this.mongoClient) {\r\n try {\r\n await this.mongoClient.close();\r\n\r\n logger.info(`[MongoDB][Close] Connection closed.`);\r\n } catch (error) {\r\n logger.error(\r\n `[MongoDB][Close] Error closing connection: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to close MongoDB connection.\");\r\n } finally {\r\n this.mongoClient = null;\r\n this.mongoDb = null;\r\n }\r\n } else {\r\n logger.warn(`[MongoDB][Close] No client to disconnect.`);\r\n throw new BadRequestError(\"No MongoDB connection exists to close.\");\r\n }\r\n }\r\n}\r\n","import Redis from \"ioredis\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nlet redisClient: Redis | null = null;\r\n\r\ntype RedisOptions = {\r\n host?: string;\r\n port?: number;\r\n username?: string;\r\n password?: string;\r\n};\r\n\r\nexport function useRedis() {\r\n function initialize(options: RedisOptions) {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n options.host = options.host ?? \"localhost\";\r\n options.port = options.port ?? 6379;\r\n options.username = options.username ?? \"default\";\r\n options.password = options.password ?? \"\";\r\n\r\n redisClient = new Redis({\r\n host: options.host,\r\n port: options.port,\r\n ...(options.username && { username: options.username }),\r\n ...(options.password && { password: options.password }),\r\n });\r\n\r\n redisClient.on(\"connect\", () => {\r\n logger.info(\r\n `[Redis][Connect] Redis client connected at ${options.host}:${options.port}.`\r\n );\r\n });\r\n\r\n redisClient.on(\"error\", (error) => {\r\n logger.error(\r\n `[Redis][Error] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n });\r\n\r\n return redisClient;\r\n }\r\n\r\n function getClient(): Redis {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new BadRequestError(\r\n \"Redis connection is not initialized. Please call initialize() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n }\r\n\r\n function disconnect() {\r\n if (redisClient) {\r\n redisClient.quit();\r\n logger.info(\"[Redis][Disconnect] Redis connection has been closed.\");\r\n redisClient = null;\r\n } else {\r\n logger.warn(\"[Redis][Disconnect] No Redis client to disconnect.\");\r\n }\r\n }\r\n\r\n return {\r\n initialize,\r\n getClient,\r\n disconnect,\r\n };\r\n}\r\n","import { useRedis } from \"./ioredis\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_TTL = 300;\r\n\r\nexport function useCache() {\r\n function getRedisClient() {\r\n return useRedis().getClient();\r\n }\r\n\r\n async function get<T = unknown>(cacheKey: string): Promise<T | null> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const value = await redisClient.get(cacheKey);\r\n\r\n return value ? (JSON.parse(value) as T) : null;\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Get] Failed to retrieve key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n\r\n return null;\r\n }\r\n }\r\n\r\n async function set<T = unknown>(\r\n cacheKey: string,\r\n data: T,\r\n ttl: number = DEFAULT_TTL,\r\n group?: string\r\n ): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.set(cacheKey, JSON.stringify(data), \"EX\", ttl);\r\n logger.info(`[Cache][Set] Stored key \"${cacheKey}\" with TTL ${ttl}s.`);\r\n\r\n if (group) await redisClient.sadd(`cache:group:${group}`, cacheKey);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Set] Failed to store key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function remove(cacheKey: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.del(cacheKey);\r\n logger.info(`[Cache][Remove] Key \"${cacheKey}\" has been deleted.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Remove] Failed to delete key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function clearGroup(group: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const keys = await redisClient.smembers(`cache:group:${group}`);\r\n if (keys.length) await redisClient.del(...keys);\r\n\r\n await redisClient.del(`cache:group:${group}`);\r\n logger.info(`[Cache][ClearGroup] Cleared group \"${group}\" and its keys.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][ClearGroup] Failed to clear group \"${group}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n return {\r\n get,\r\n set,\r\n remove,\r\n clearGroup,\r\n };\r\n}\r\n","export function buildCacheKey(\r\n prefix: string,\r\n values: Record<string, any>\r\n): string {\r\n const query = Object.entries(values)\r\n .sort(([a], [b]) => a.localeCompare(b))\r\n .map(\r\n ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`\r\n )\r\n .join(\"&\");\r\n\r\n return `${prefix}:${query}`;\r\n}\r\n","import path from \"path\";\r\n\r\nexport function getTemplatePath(directory: string, filePath: string) {\r\n const ext = \".hbs\";\r\n const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;\r\n\r\n // Resolve from project root, assuming templates are in src/public/templates or public/templates\r\n const possiblePaths = [\r\n path.join(process.cwd(), \"src\", \"public\", directory, file),\r\n path.join(process.cwd(), \"public\", directory, file),\r\n path.join(process.cwd(), directory, file),\r\n ];\r\n\r\n // Return the first path that exists, or default to src/public structure\r\n const fs = require(\"fs\");\r\n for (const templatePath of possiblePaths) {\r\n if (fs.existsSync(templatePath)) {\r\n return templatePath;\r\n }\r\n }\r\n\r\n // Default to src/public structure if none exist\r\n return possiblePaths[0];\r\n}\r\n","import Handlebars from \"handlebars\";\r\nimport { promises as fs } from \"fs\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface CompileOptions {\r\n context?: Record<string, any>;\r\n filePath: string;\r\n}\r\n\r\nconst handlebarsTemplateCache = new Map<string, Handlebars.TemplateDelegate>();\r\n\r\n// Register Handlebars helpers\r\nHandlebars.registerHelper(\"formatCurrency\", function (amount: number) {\r\n if (typeof amount !== \"number\") return \"0.00\";\r\n return new Intl.NumberFormat(\"en-PH\", {\r\n style: \"decimal\",\r\n minimumFractionDigits: 2,\r\n maximumFractionDigits: 2,\r\n }).format(amount);\r\n});\r\n\r\nHandlebars.registerHelper(\r\n \"formatDate\",\r\n function (date: Date | string, format: string) {\r\n if (!date) return \"N/A\";\r\n\r\n const dateObj = typeof date === \"string\" ? new Date(date) : date;\r\n if (isNaN(dateObj.getTime())) return \"N/A\";\r\n\r\n const options: Intl.DateTimeFormatOptions = {};\r\n\r\n if (format.includes(\"MMMM\")) options.month = \"long\";\r\n else if (format.includes(\"MMM\")) options.month = \"short\";\r\n else if (format.includes(\"MM\")) options.month = \"2-digit\";\r\n\r\n if (format.includes(\"D,\")) options.day = \"numeric\";\r\n else if (format.includes(\"DD\")) options.day = \"2-digit\";\r\n\r\n if (format.includes(\"YYYY\")) options.year = \"numeric\";\r\n else if (format.includes(\"YY\")) options.year = \"2-digit\";\r\n\r\n if (format.includes(\"h:mm A\")) {\r\n options.hour = \"numeric\";\r\n options.minute = \"2-digit\";\r\n options.hour12 = true;\r\n } else if (format.includes(\"HH:mm\")) {\r\n options.hour = \"2-digit\";\r\n options.minute = \"2-digit\";\r\n options.hour12 = false;\r\n }\r\n\r\n return new Intl.DateTimeFormat(\"en-US\", options).format(dateObj);\r\n }\r\n);\r\n\r\nexport async function renderHandlebarsTemplate({\r\n context = {},\r\n filePath,\r\n}: CompileOptions): Promise<string> {\r\n try {\r\n let compiledTemplate = handlebarsTemplateCache.get(filePath);\r\n\r\n if (!compiledTemplate) {\r\n logger.info(\r\n `[Template][Compile] Compiling and caching template from \"${filePath}\".`\r\n );\r\n const fileContent = await fs.readFile(filePath, \"utf8\");\r\n compiledTemplate = Handlebars.compile(fileContent);\r\n handlebarsTemplateCache.set(filePath, compiledTemplate);\r\n } else {\r\n logger.info(`[Template][Cache] Using cached template for \"${filePath}\".`);\r\n }\r\n\r\n return compiledTemplate(context);\r\n } catch (error) {\r\n logger.error(\r\n `[Template][Render] Failed to render template \"${filePath}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to render Handlebars template.\");\r\n }\r\n}\r\n","import jwt from \"jsonwebtoken\";\r\nimport { BadRequestError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface JwtSignParams {\r\n payload?: Record<string, unknown>;\r\n secretKey: string;\r\n signOptions?: jwt.SignOptions;\r\n}\r\n\r\nexport function signJwtToken({\r\n payload = {},\r\n secretKey = \"\",\r\n signOptions = {},\r\n}: JwtSignParams): string {\r\n if (!secretKey) {\r\n logger.error(`[JWT][Sign] Secret key is missing. Cannot sign token.`);\r\n throw new BadRequestError(\"A JWT secret key must be provided.\");\r\n }\r\n\r\n try {\r\n logger.info(`[JWT][Sign] Signing JWT token.`);\r\n return jwt.sign(payload, secretKey, signOptions);\r\n } catch (error) {\r\n logger.error(\r\n `[JWT][Sign] Failed to sign JWT token: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw error;\r\n }\r\n}\r\n","import { createTransport, SendMailOptions, Transporter } from \"nodemailer\";\r\nimport { logger } from \"./logger\";\r\nimport { InternalServerError } from \"./http-error\";\r\n\r\ninterface MailerConfig {\r\n email: string;\r\n password: string;\r\n host: string;\r\n port: number;\r\n secure: boolean;\r\n}\r\n\r\nexport class useMailer {\r\n private transporter: Transporter;\r\n\r\n constructor(private config: MailerConfig) {\r\n this.transporter = createTransport({\r\n host: config.host,\r\n port: config.port,\r\n secure: config.secure,\r\n auth: { user: config.email, pass: config.password },\r\n });\r\n }\r\n\r\n async sendMail({\r\n sender,\r\n to,\r\n subject,\r\n text,\r\n html,\r\n }: {\r\n sender?: string;\r\n to: string;\r\n subject: string;\r\n text?: string;\r\n html?: string;\r\n }): Promise<string> {\r\n const from = sender\r\n ? `${sender} <${this.config.email}>`\r\n : this.config.email;\r\n\r\n const mailOptions: SendMailOptions = {\r\n from,\r\n to,\r\n subject,\r\n ...(text && { text }),\r\n ...(html && { html }),\r\n };\r\n\r\n try {\r\n await this.transporter.sendMail(mailOptions);\r\n logger.info(\r\n `[Mailer][Send] Email sent to \"${to}\" with subject \"${subject}\".`\r\n );\r\n\r\n return \"Mail sent successfully.\";\r\n } catch (error) {\r\n logger.error(\r\n `[Mailer][Send] Failed to send email to \"${to}\" with subject \"${subject}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to send email.\");\r\n }\r\n }\r\n}\r\n","import { ObjectId } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nexport function toObjectId(id: string | ObjectId): ObjectId {\r\n if (!id) {\r\n logger.error(\r\n `[ObjectId][Convert] No value provided for MongoDB ObjectId conversion.`\r\n );\r\n throw new BadRequestError(\r\n \"A value must be provided for ObjectId conversion.\"\r\n );\r\n }\r\n\r\n if (id instanceof ObjectId) return id;\r\n\r\n if (!/^[0-9a-fA-F]{24}$/.test(id)) {\r\n logger.error(\r\n `[ObjectId][Convert] Provided value is not a valid 24-character hex string.`\r\n );\r\n throw new BadRequestError(\r\n \"Invalid ObjectId: must be a 24-character hexadecimal string.\"\r\n );\r\n }\r\n\r\n try {\r\n return new ObjectId(id);\r\n } catch (err) {\r\n logger.error(\r\n `[ObjectId][Convert] Failed to convert value to ObjectId: ${\r\n err instanceof Error ? err.message : err\r\n }`\r\n );\r\n throw new BadRequestError(\"Failed to convert value into a valid ObjectId.\");\r\n }\r\n}\r\n","export function paginate<T>(\r\n items: T[],\r\n page: number = 0,\r\n limit: number = 10,\r\n total: number\r\n) {\r\n if (total === 0 || items.length === 0) {\r\n return {\r\n items: [],\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n\r\n const startIndex = (page - 1) * limit + 1;\r\n if (startIndex > total) {\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n const endIndex = Math.min(startIndex + items.length - 1, total);\r\n\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `${startIndex}-${endIndex} of ${total}`,\r\n };\r\n}\r\n","import bcrypt from \"bcrypt\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_SALT_ROUNDS = 10;\r\n\r\nexport async function comparePasswords(\r\n password: string,\r\n hashed: string\r\n): Promise<boolean> {\r\n try {\r\n return await bcrypt.compare(password, hashed);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Compare] Failed to compare passwords: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n return false;\r\n }\r\n}\r\n\r\nexport async function hashPassword(\r\n password: string,\r\n saltRounds: number = DEFAULT_SALT_ROUNDS\r\n): Promise<string> {\r\n try {\r\n return await bcrypt.hash(password, saltRounds);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Hash] Failed to hash password: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to hash password.\");\r\n }\r\n}\r\n","import { createClient, RedisClientType } from \"redis\";\r\nimport { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from \"./../config\";\r\nimport { logger } from \"./logger\";\r\n\r\nlet redisClient: RedisClientType | null = null;\r\n\r\nexport async function initRedisClient() {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n redisClient = createClient({\r\n password: REDIS_PASSWORD,\r\n socket: {\r\n host: REDIS_HOST,\r\n port: REDIS_PORT,\r\n },\r\n });\r\n}\r\n\r\nexport function useRedisClient(): RedisClientType {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new Error(\r\n \"[Redis][GetClient] Redis connection is not initialized. Call initRedisClient() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n}\r\n","import * as dotenv from \"dotenv\";\r\n\r\ndotenv.config();\r\n\r\nfunction getEnv(key: string, fallback?: string): string {\r\n const value = process.env[key];\r\n if (value !== undefined) return value as string;\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvNumber(key: string, fallback?: number): number {\r\n const value = process.env[key];\r\n if (value !== undefined) return Number(value);\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvBoolean(key: string, fallback?: boolean): boolean {\r\n const value = process.env[key];\r\n if (value !== undefined) return value === \"true\";\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\n// Redis\r\nexport const REDIS_HOST = getEnv(\"REDIS_HOST\");\r\nexport const REDIS_PORT = getEnvNumber(\"REDIS_PORT\", 6379);\r\nexport const REDIS_PASSWORD = getEnv(\"REDIS_PASSWORD\");\r\nexport const REDIS_TLS = getEnvBoolean(\"REDIS_TLS\", true);\r\n","import {\r\n DeleteObjectCommand,\r\n PutObjectCommand,\r\n S3Client,\r\n} from \"@aws-sdk/client-s3\";\r\nimport { Readable } from \"stream\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface S3Config {\r\n accessKeyId: string;\r\n secretAccessKey: string;\r\n endpoint: string;\r\n region: string;\r\n bucket: string;\r\n forcePathStyle: boolean;\r\n}\r\n\r\nexport class useS3 {\r\n private client: S3Client;\r\n\r\n constructor(private config: S3Config) {\r\n this.client = new S3Client({\r\n endpoint: config.endpoint,\r\n region: config.region,\r\n credentials: {\r\n accessKeyId: config.accessKeyId,\r\n secretAccessKey: config.secretAccessKey,\r\n },\r\n forcePathStyle: config.forcePathStyle,\r\n });\r\n }\r\n\r\n async uploadObject({\r\n key,\r\n body,\r\n metadata = {},\r\n contentType,\r\n }: {\r\n key: string;\r\n body: string | Buffer;\r\n metadata?: Record<string, string>;\r\n contentType?: string;\r\n }): Promise<string> {\r\n try {\r\n await this.client.send(\r\n new PutObjectCommand({\r\n Bucket: this.config.bucket,\r\n Key: key,\r\n Body:\r\n typeof body === \"string\" || Buffer.isBuffer(body)\r\n ? body\r\n : Readable.from(body),\r\n ACL: \"public-read\",\r\n Metadata: metadata,\r\n ContentType: contentType,\r\n ContentLength:\r\n typeof body === \"string\" ? Buffer.byteLength(body) : body.length,\r\n })\r\n );\r\n\r\n logger.info(\r\n `[S3][Upload] Uploaded \"${key}\" to bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully uploaded file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Upload][Error] Failed to upload \"${key}\" to bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n async deleteObject(key: string) {\r\n try {\r\n await this.client.send(\r\n new DeleteObjectCommand({ Key: key, Bucket: this.config.bucket })\r\n );\r\n\r\n logger.info(\r\n `[S3][Delete] Deleted \"${key}\" from bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully deleted file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Delete][Error] Failed to delete \"${key}\" from bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n}\r\n","import crypto from \"crypto\";\r\n\r\n// Deterministic hash for tokens at rest (do NOT use bcrypt here)\r\n// Optionally salt via env if provided\r\nconst TOKEN_HASH_SALT = process.env.REFRESH_TOKEN_HASH_SALT || \"\";\r\n\r\nexport function hashToken(token: string): string {\r\n return crypto\r\n .createHash(\"sha256\")\r\n .update(TOKEN_HASH_SALT + token)\r\n .digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":["transports","jwt","config","Redis","redisClient","path","fs","Handlebars","format","fs","import_jsonwebtoken","jwt","config","import_mongodb","bcrypt","redisClient","config","crypto"]}
|
package/dist/index.mjs
CHANGED
|
@@ -17,6 +17,12 @@ var __spreadValues = (a, b) => {
|
|
|
17
17
|
return a;
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
21
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
22
|
+
}) : x)(function(x) {
|
|
23
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
24
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
25
|
+
});
|
|
20
26
|
var __async = (__this, __arguments, generator) => {
|
|
21
27
|
return new Promise((resolve, reject) => {
|
|
22
28
|
var fulfilled = (value) => {
|
|
@@ -105,40 +111,191 @@ var InternalServerError = class extends HttpError {
|
|
|
105
111
|
};
|
|
106
112
|
|
|
107
113
|
// src/middleware/auth.middleware.ts
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
function extractToken(authHeader) {
|
|
115
|
+
logger.debug("Attempting to extract token from Authorization header");
|
|
116
|
+
if (!authHeader) {
|
|
117
|
+
logger.debug("No Authorization header provided");
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (!authHeader.startsWith("Bearer ")) {
|
|
121
|
+
logger.warn("Authorization header does not start with 'Bearer '", {
|
|
122
|
+
authHeader: authHeader.substring(0, 20) + "..."
|
|
123
|
+
});
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const token = authHeader.split(" ")[1] || null;
|
|
127
|
+
logger.debug("Token extraction result", {
|
|
128
|
+
hasToken: !!token,
|
|
129
|
+
tokenLength: token == null ? void 0 : token.length
|
|
130
|
+
});
|
|
131
|
+
return token;
|
|
132
|
+
}
|
|
133
|
+
function authenticateToken(req, options) {
|
|
134
|
+
return __async(this, null, function* () {
|
|
135
|
+
const {
|
|
136
|
+
secretKey = process.env.ACCESS_TOKEN_SECRET || "",
|
|
137
|
+
isTokenRevoked,
|
|
138
|
+
requiredRole
|
|
139
|
+
} = options;
|
|
140
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
141
|
+
logger.info("Starting authentication process", {
|
|
142
|
+
requestId,
|
|
143
|
+
path: req.path,
|
|
144
|
+
method: req.method,
|
|
145
|
+
requiredRole,
|
|
146
|
+
hasRevocationCheck: !!isTokenRevoked
|
|
147
|
+
});
|
|
148
|
+
const token = extractToken(req.headers.authorization);
|
|
112
149
|
if (!token) {
|
|
113
|
-
logger.error("No access token provided
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
);
|
|
150
|
+
logger.error("Authentication failed: No access token provided", {
|
|
151
|
+
requestId,
|
|
152
|
+
path: req.path
|
|
153
|
+
});
|
|
154
|
+
throw new UnauthorizedError("Access token is required to proceed.");
|
|
117
155
|
}
|
|
156
|
+
logger.debug("Token extracted successfully, proceeding with verification", {
|
|
157
|
+
requestId,
|
|
158
|
+
tokenLength: token.length
|
|
159
|
+
});
|
|
160
|
+
let decoded;
|
|
118
161
|
try {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
162
|
+
logger.debug("Verifying JWT token", { requestId });
|
|
163
|
+
decoded = jwt.verify(token, secretKey);
|
|
164
|
+
logger.info("JWT token verified successfully", {
|
|
165
|
+
requestId,
|
|
166
|
+
userId: decoded.user || decoded.id,
|
|
167
|
+
role: decoded.role,
|
|
168
|
+
jti: decoded.jti,
|
|
169
|
+
exp: decoded.exp ? new Date(decoded.exp * 1e3).toISOString() : "unknown"
|
|
170
|
+
});
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logger.error("JWT verification failed", {
|
|
173
|
+
requestId,
|
|
174
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
175
|
+
tokenLength: token.length
|
|
176
|
+
});
|
|
177
|
+
throw new UnauthorizedError(
|
|
178
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
if (isTokenRevoked && decoded.jti) {
|
|
182
|
+
logger.debug("Checking token revocation status", {
|
|
183
|
+
requestId,
|
|
184
|
+
jti: decoded.jti
|
|
185
|
+
});
|
|
186
|
+
const isRevoked = yield isTokenRevoked(decoded.jti);
|
|
187
|
+
if (isRevoked) {
|
|
188
|
+
logger.warn("Authentication failed: Token is revoked", {
|
|
189
|
+
requestId,
|
|
190
|
+
jti: decoded.jti,
|
|
191
|
+
userId: decoded.user || decoded.id
|
|
123
192
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"Your session has expired or the token is invalid. Please log in again."
|
|
127
|
-
)
|
|
193
|
+
throw new UnauthorizedError(
|
|
194
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
128
195
|
);
|
|
129
196
|
}
|
|
130
|
-
|
|
131
|
-
|
|
197
|
+
logger.debug("Token revocation check passed", {
|
|
198
|
+
requestId,
|
|
199
|
+
jti: decoded.jti
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (requiredRole && decoded.role !== requiredRole) {
|
|
203
|
+
logger.warn("Authorization failed: Insufficient permissions", {
|
|
204
|
+
requestId,
|
|
205
|
+
userId: decoded.user || decoded.id,
|
|
206
|
+
userRole: decoded.role,
|
|
207
|
+
requiredRole,
|
|
208
|
+
path: req.path
|
|
209
|
+
});
|
|
210
|
+
throw new ForbiddenError(
|
|
211
|
+
"Insufficient permissions to access this resource."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
req.user = __spreadProps(__spreadValues({}, decoded), {
|
|
215
|
+
id: decoded.user || decoded.id || ""
|
|
216
|
+
});
|
|
217
|
+
req.token = token;
|
|
218
|
+
logger.info("Authentication completed successfully", {
|
|
219
|
+
requestId,
|
|
220
|
+
userId: req.user.id,
|
|
221
|
+
role: req.user.role,
|
|
222
|
+
path: req.path,
|
|
223
|
+
method: req.method
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function authenticate(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
228
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
229
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
230
|
+
logger.debug("Authenticate middleware invoked", {
|
|
231
|
+
requestId,
|
|
232
|
+
path: req.path
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
yield authenticateToken(req, { secretKey, isTokenRevoked });
|
|
236
|
+
logger.debug("Authenticate middleware completed successfully", {
|
|
237
|
+
requestId
|
|
132
238
|
});
|
|
133
|
-
req.token = token;
|
|
134
239
|
next();
|
|
135
240
|
} catch (error) {
|
|
136
|
-
logger.error("
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
241
|
+
logger.error("Authenticate middleware failed", {
|
|
242
|
+
requestId,
|
|
243
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
244
|
+
});
|
|
245
|
+
next(error);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function requireAdmin(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
250
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
251
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
252
|
+
logger.debug("RequireAdmin middleware invoked", {
|
|
253
|
+
requestId,
|
|
254
|
+
path: req.path
|
|
255
|
+
});
|
|
256
|
+
try {
|
|
257
|
+
yield authenticateToken(req, {
|
|
258
|
+
secretKey,
|
|
259
|
+
isTokenRevoked,
|
|
260
|
+
requiredRole: "admin"
|
|
261
|
+
});
|
|
262
|
+
logger.debug("RequireAdmin middleware completed successfully", {
|
|
263
|
+
requestId
|
|
264
|
+
});
|
|
265
|
+
next();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
logger.error("RequireAdmin middleware failed", {
|
|
268
|
+
requestId,
|
|
269
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
270
|
+
});
|
|
271
|
+
next(error);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function requireRole(role, options) {
|
|
276
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
277
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
278
|
+
logger.debug("RequireRole middleware invoked", {
|
|
279
|
+
requestId,
|
|
280
|
+
path: req.path,
|
|
281
|
+
requiredRole: role
|
|
282
|
+
});
|
|
283
|
+
try {
|
|
284
|
+
yield authenticateToken(req, __spreadProps(__spreadValues({}, options), {
|
|
285
|
+
requiredRole: role
|
|
286
|
+
}));
|
|
287
|
+
logger.debug("RequireRole middleware completed successfully", {
|
|
288
|
+
requestId,
|
|
289
|
+
role
|
|
290
|
+
});
|
|
291
|
+
next();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
logger.error("RequireRole middleware failed", {
|
|
294
|
+
requestId,
|
|
295
|
+
requiredRole: role,
|
|
296
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
297
|
+
});
|
|
298
|
+
next(error);
|
|
142
299
|
}
|
|
143
300
|
});
|
|
144
301
|
}
|
|
@@ -375,13 +532,58 @@ import path from "path";
|
|
|
375
532
|
function getTemplatePath(directory, filePath) {
|
|
376
533
|
const ext = ".hbs";
|
|
377
534
|
const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;
|
|
378
|
-
|
|
535
|
+
const possiblePaths = [
|
|
536
|
+
path.join(process.cwd(), "src", "public", directory, file),
|
|
537
|
+
path.join(process.cwd(), "public", directory, file),
|
|
538
|
+
path.join(process.cwd(), directory, file)
|
|
539
|
+
];
|
|
540
|
+
const fs2 = __require("fs");
|
|
541
|
+
for (const templatePath of possiblePaths) {
|
|
542
|
+
if (fs2.existsSync(templatePath)) {
|
|
543
|
+
return templatePath;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return possiblePaths[0];
|
|
379
547
|
}
|
|
380
548
|
|
|
381
549
|
// src/utils/handlebars-compiler.ts
|
|
382
550
|
import Handlebars from "handlebars";
|
|
383
551
|
import { promises as fs } from "fs";
|
|
384
552
|
var handlebarsTemplateCache = /* @__PURE__ */ new Map();
|
|
553
|
+
Handlebars.registerHelper("formatCurrency", function(amount) {
|
|
554
|
+
if (typeof amount !== "number") return "0.00";
|
|
555
|
+
return new Intl.NumberFormat("en-PH", {
|
|
556
|
+
style: "decimal",
|
|
557
|
+
minimumFractionDigits: 2,
|
|
558
|
+
maximumFractionDigits: 2
|
|
559
|
+
}).format(amount);
|
|
560
|
+
});
|
|
561
|
+
Handlebars.registerHelper(
|
|
562
|
+
"formatDate",
|
|
563
|
+
function(date, format2) {
|
|
564
|
+
if (!date) return "N/A";
|
|
565
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
566
|
+
if (isNaN(dateObj.getTime())) return "N/A";
|
|
567
|
+
const options = {};
|
|
568
|
+
if (format2.includes("MMMM")) options.month = "long";
|
|
569
|
+
else if (format2.includes("MMM")) options.month = "short";
|
|
570
|
+
else if (format2.includes("MM")) options.month = "2-digit";
|
|
571
|
+
if (format2.includes("D,")) options.day = "numeric";
|
|
572
|
+
else if (format2.includes("DD")) options.day = "2-digit";
|
|
573
|
+
if (format2.includes("YYYY")) options.year = "numeric";
|
|
574
|
+
else if (format2.includes("YY")) options.year = "2-digit";
|
|
575
|
+
if (format2.includes("h:mm A")) {
|
|
576
|
+
options.hour = "numeric";
|
|
577
|
+
options.minute = "2-digit";
|
|
578
|
+
options.hour12 = true;
|
|
579
|
+
} else if (format2.includes("HH:mm")) {
|
|
580
|
+
options.hour = "2-digit";
|
|
581
|
+
options.minute = "2-digit";
|
|
582
|
+
options.hour12 = false;
|
|
583
|
+
}
|
|
584
|
+
return new Intl.DateTimeFormat("en-US", options).format(dateObj);
|
|
585
|
+
}
|
|
586
|
+
);
|
|
385
587
|
function renderHandlebarsTemplate(_0) {
|
|
386
588
|
return __async(this, arguments, function* ({
|
|
387
589
|
context = {},
|
|
@@ -711,6 +913,8 @@ export {
|
|
|
711
913
|
logger,
|
|
712
914
|
paginate,
|
|
713
915
|
renderHandlebarsTemplate,
|
|
916
|
+
requireAdmin,
|
|
917
|
+
requireRole,
|
|
714
918
|
signJwtToken,
|
|
715
919
|
toObjectId,
|
|
716
920
|
useAtlas,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware/auth.middleware.ts","../src/utils/logger.ts","../src/utils/http-error.ts","../src/middleware/error-handler.middleware.ts","../src/utils/atlas.ts","../src/utils/ioredis.ts","../src/utils/cache.ts","../src/utils/cache-key.ts","../src/utils/get-template-path.ts","../src/utils/handlebars-compiler.ts","../src/utils/jwt.ts","../src/utils/mailer.ts","../src/utils/objectid-converter.ts","../src/utils/paginate.ts","../src/utils/password.ts","../src/utils/redis.ts","../src/config.ts","../src/utils/s3.ts","../src/utils/token.ts"],"sourcesContent":["import { NextFunction, Request, Response } from \"express\";\r\nimport jwt, { JwtPayload } from \"jsonwebtoken\";\r\nimport { logger } from \"./../utils/logger\";\r\nimport { UnauthorizedError } from \"./../utils/http-error\";\r\n\r\nexport interface AuthenticatedRequest extends Request {\r\n user?: JwtPayload & { user?: string; id?: string };\r\n token?: string;\r\n}\r\n\r\nexport function authenticate(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const authHeader = req.headers.authorization;\r\n const token = authHeader?.split(\" \")[1];\r\n\r\n if (!token) {\r\n logger.error(\"No access token provided in the request.\");\r\n return next(\r\n new UnauthorizedError(\"Access token is required to proceed.\")\r\n );\r\n }\r\n\r\n try {\r\n const decoded = jwt.verify(token, secretKey) as JwtPayload & {\r\n user?: string;\r\n id?: string;\r\n jti?: string;\r\n };\r\n\r\n if (\r\n isTokenRevoked &&\r\n decoded.jti &&\r\n (await isTokenRevoked(decoded.jti))\r\n ) {\r\n logger.warn(\"Access token is revoked (blacklisted).\", {\r\n jti: decoded.jti,\r\n });\r\n return next(\r\n new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n )\r\n );\r\n }\r\n\r\n req.user = {\r\n ...(decoded as any),\r\n id: (decoded as any).user || (decoded as any).id,\r\n };\r\n req.token = token;\r\n next();\r\n } catch (error) {\r\n logger.error(\"Failed to verify access token.\", error);\r\n return next(\r\n new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n )\r\n );\r\n }\r\n };\r\n}\r\n","import * as winston from \"winston\";\r\n\r\nconst transports = [\r\n new winston.transports.File({ filename: \"error.log\", level: \"error\" }),\r\n new winston.transports.File({ filename: \"combined.log\" }),\r\n];\r\n\r\nexport const logger = winston.createLogger({\r\n level: \"info\",\r\n format: winston.format.combine(\r\n winston.format.timestamp(),\r\n winston.format.json()\r\n ),\r\n transports,\r\n});\r\n","export class HttpError extends Error {\r\n public readonly statusCode: number;\r\n public readonly isOperational: boolean;\r\n\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n isOperational: boolean = true\r\n ) {\r\n super(message);\r\n\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n\r\n this.statusCode = statusCode;\r\n this.isOperational = isOperational;\r\n\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, this.constructor);\r\n }\r\n }\r\n}\r\n\r\nexport class BadRequestError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be processed. Please review your input and try again.\"\r\n ) {\r\n super(message, 400, true);\r\n }\r\n}\r\n\r\nexport class UnauthorizedError extends HttpError {\r\n constructor(\r\n message: string = \"Authentication is required to access this resource.\"\r\n ) {\r\n super(message, 401, true);\r\n }\r\n}\r\n\r\nexport class ForbiddenError extends HttpError {\r\n constructor(\r\n message: string = \"You do not have the necessary permissions to perform this action.\"\r\n ) {\r\n super(message, 403, true);\r\n }\r\n}\r\n\r\nexport class NotFoundError extends HttpError {\r\n constructor(message: string = \"The requested resource could not be found.\") {\r\n super(message, 404, true);\r\n }\r\n}\r\n\r\nexport class ConflictError extends HttpError {\r\n constructor(\r\n message: string = \"A resource with the provided values already exists.\"\r\n ) {\r\n super(message, 409, true);\r\n }\r\n}\r\n\r\nexport class UnprocessableEntityError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be completed due to invalid or incomplete information.\"\r\n ) {\r\n super(message, 422, true);\r\n }\r\n}\r\n\r\nexport class InternalServerError extends HttpError {\r\n constructor(\r\n message: string = \"An internal server error occurred. Please try again later.\"\r\n ) {\r\n super(message, 500, true);\r\n }\r\n}\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport { HttpError, InternalServerError } from \"./../utils/http-error\";\r\nimport { logger } from \"./../utils/logger\";\r\n\r\nexport const errorHandler = (\r\n error: HttpError,\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n) => {\r\n if (error.isOperational) {\r\n res\r\n .status(error.statusCode)\r\n .json({ status: \"error\", message: error.message });\r\n } else {\r\n logger.error({ message: error.message });\r\n res\r\n .status(500)\r\n .json({ status: \"error\", message: new InternalServerError().message });\r\n }\r\n\r\n return;\r\n};\r\n","import { Db, MongoClient } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport {\r\n BadRequestError,\r\n InternalServerError,\r\n NotFoundError,\r\n} from \"./http-error\";\r\n\r\ninterface AtlasConfig {\r\n uri: string;\r\n db: string;\r\n name?: string;\r\n}\r\n\r\nexport class useAtlas {\r\n private static mongoClient: MongoClient | null = null;\r\n private static mongoDb: Db | null = null;\r\n\r\n public static async connect(config: AtlasConfig): Promise<void> {\r\n if (this.mongoClient) {\r\n logger.warn(\r\n \"[MongoDB][Connect] Client is already connected. Skipping new connection.\"\r\n );\r\n throw new BadRequestError(\"A MongoDB connection is already established.\");\r\n }\r\n\r\n const { db, uri } = config;\r\n this.mongoClient = new MongoClient(uri, {\r\n maxPoolSize: 10,\r\n maxIdleTimeMS: 60000,\r\n connectTimeoutMS: 60000,\r\n });\r\n\r\n try {\r\n await this.mongoClient.connect();\r\n this.mongoDb = this.mongoClient.db(db);\r\n\r\n logger.info(\r\n `[MongoDB][Connect] Connected to database \"${this.mongoDb.databaseName}\".`\r\n );\r\n } catch (error) {\r\n this.mongoClient = null;\r\n\r\n logger.error(\r\n `[MongoDB][Connect] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\r\n \"Failed to connect to the database. Please try again later.\"\r\n );\r\n }\r\n }\r\n\r\n public static getClient(): MongoClient | null {\r\n if (!this.mongoClient) {\r\n logger.warn(`[MongoDB][GetClient] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return this.mongoClient;\r\n }\r\n\r\n public static getDb(): Db | null {\r\n if (!this.mongoDb) {\r\n logger.warn(`[MongoDB][GetDb] Database instance is not available.`);\r\n throw new NotFoundError(\"MongoDB database instance is not available.\");\r\n }\r\n\r\n return this.mongoDb;\r\n }\r\n\r\n public static startSession(): import(\"mongodb\").ClientSession {\r\n const client = this.getClient();\r\n\r\n if (!client) {\r\n logger.warn(`[MongoDB][StartSession] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return client.startSession();\r\n }\r\n\r\n public static async close(): Promise<void> {\r\n if (this.mongoClient) {\r\n try {\r\n await this.mongoClient.close();\r\n\r\n logger.info(`[MongoDB][Close] Connection closed.`);\r\n } catch (error) {\r\n logger.error(\r\n `[MongoDB][Close] Error closing connection: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to close MongoDB connection.\");\r\n } finally {\r\n this.mongoClient = null;\r\n this.mongoDb = null;\r\n }\r\n } else {\r\n logger.warn(`[MongoDB][Close] No client to disconnect.`);\r\n throw new BadRequestError(\"No MongoDB connection exists to close.\");\r\n }\r\n }\r\n}\r\n","import Redis from \"ioredis\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nlet redisClient: Redis | null = null;\r\n\r\ntype RedisOptions = {\r\n host?: string;\r\n port?: number;\r\n username?: string;\r\n password?: string;\r\n};\r\n\r\nexport function useRedis() {\r\n function initialize(options: RedisOptions) {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n options.host = options.host ?? \"localhost\";\r\n options.port = options.port ?? 6379;\r\n options.username = options.username ?? \"default\";\r\n options.password = options.password ?? \"\";\r\n\r\n redisClient = new Redis({\r\n host: options.host,\r\n port: options.port,\r\n ...(options.username && { username: options.username }),\r\n ...(options.password && { password: options.password }),\r\n });\r\n\r\n redisClient.on(\"connect\", () => {\r\n logger.info(\r\n `[Redis][Connect] Redis client connected at ${options.host}:${options.port}.`\r\n );\r\n });\r\n\r\n redisClient.on(\"error\", (error) => {\r\n logger.error(\r\n `[Redis][Error] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n });\r\n\r\n return redisClient;\r\n }\r\n\r\n function getClient(): Redis {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new BadRequestError(\r\n \"Redis connection is not initialized. Please call initialize() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n }\r\n\r\n function disconnect() {\r\n if (redisClient) {\r\n redisClient.quit();\r\n logger.info(\"[Redis][Disconnect] Redis connection has been closed.\");\r\n redisClient = null;\r\n } else {\r\n logger.warn(\"[Redis][Disconnect] No Redis client to disconnect.\");\r\n }\r\n }\r\n\r\n return {\r\n initialize,\r\n getClient,\r\n disconnect,\r\n };\r\n}\r\n","import { useRedis } from \"./ioredis\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_TTL = 300;\r\n\r\nexport function useCache() {\r\n function getRedisClient() {\r\n return useRedis().getClient();\r\n }\r\n\r\n async function get<T = unknown>(cacheKey: string): Promise<T | null> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const value = await redisClient.get(cacheKey);\r\n\r\n return value ? (JSON.parse(value) as T) : null;\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Get] Failed to retrieve key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n\r\n return null;\r\n }\r\n }\r\n\r\n async function set<T = unknown>(\r\n cacheKey: string,\r\n data: T,\r\n ttl: number = DEFAULT_TTL,\r\n group?: string\r\n ): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.set(cacheKey, JSON.stringify(data), \"EX\", ttl);\r\n logger.info(`[Cache][Set] Stored key \"${cacheKey}\" with TTL ${ttl}s.`);\r\n\r\n if (group) await redisClient.sadd(`cache:group:${group}`, cacheKey);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Set] Failed to store key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function remove(cacheKey: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.del(cacheKey);\r\n logger.info(`[Cache][Remove] Key \"${cacheKey}\" has been deleted.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Remove] Failed to delete key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function clearGroup(group: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const keys = await redisClient.smembers(`cache:group:${group}`);\r\n if (keys.length) await redisClient.del(...keys);\r\n\r\n await redisClient.del(`cache:group:${group}`);\r\n logger.info(`[Cache][ClearGroup] Cleared group \"${group}\" and its keys.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][ClearGroup] Failed to clear group \"${group}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n return {\r\n get,\r\n set,\r\n remove,\r\n clearGroup,\r\n };\r\n}\r\n","export function buildCacheKey(\r\n prefix: string,\r\n values: Record<string, any>\r\n): string {\r\n const query = Object.entries(values)\r\n .sort(([a], [b]) => a.localeCompare(b))\r\n .map(\r\n ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`\r\n )\r\n .join(\"&\");\r\n\r\n return `${prefix}:${query}`;\r\n}\r\n","import path from \"path\";\r\n\r\nexport function getTemplatePath(directory: string, filePath: string) {\r\n const ext = \".hbs\";\r\n const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;\r\n return path.resolve(directory, file);\r\n}\r\n","import Handlebars from \"handlebars\";\r\nimport { promises as fs } from \"fs\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface CompileOptions {\r\n context?: Record<string, any>;\r\n filePath: string;\r\n}\r\n\r\nconst handlebarsTemplateCache = new Map<string, Handlebars.TemplateDelegate>();\r\n\r\nexport async function renderHandlebarsTemplate({\r\n context = {},\r\n filePath,\r\n}: CompileOptions): Promise<string> {\r\n try {\r\n let compiledTemplate = handlebarsTemplateCache.get(filePath);\r\n\r\n if (!compiledTemplate) {\r\n logger.info(\r\n `[Template][Compile] Compiling and caching template from \"${filePath}\".`\r\n );\r\n const fileContent = await fs.readFile(filePath, \"utf8\");\r\n compiledTemplate = Handlebars.compile(fileContent);\r\n handlebarsTemplateCache.set(filePath, compiledTemplate);\r\n } else {\r\n logger.info(`[Template][Cache] Using cached template for \"${filePath}\".`);\r\n }\r\n\r\n return compiledTemplate(context);\r\n } catch (error) {\r\n logger.error(\r\n `[Template][Render] Failed to render template \"${filePath}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to render Handlebars template.\");\r\n }\r\n}\r\n","import jwt from \"jsonwebtoken\";\r\nimport { BadRequestError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface JwtSignParams {\r\n payload?: Record<string, unknown>;\r\n secretKey: string;\r\n signOptions?: jwt.SignOptions;\r\n}\r\n\r\nexport function signJwtToken({\r\n payload = {},\r\n secretKey = \"\",\r\n signOptions = {},\r\n}: JwtSignParams): string {\r\n if (!secretKey) {\r\n logger.error(`[JWT][Sign] Secret key is missing. Cannot sign token.`);\r\n throw new BadRequestError(\"A JWT secret key must be provided.\");\r\n }\r\n\r\n try {\r\n logger.info(`[JWT][Sign] Signing JWT token.`);\r\n return jwt.sign(payload, secretKey, signOptions);\r\n } catch (error) {\r\n logger.error(\r\n `[JWT][Sign] Failed to sign JWT token: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw error;\r\n }\r\n}\r\n","import { createTransport, SendMailOptions, Transporter } from \"nodemailer\";\r\nimport { logger } from \"./logger\";\r\nimport { InternalServerError } from \"./http-error\";\r\n\r\ninterface MailerConfig {\r\n email: string;\r\n password: string;\r\n host: string;\r\n port: number;\r\n secure: boolean;\r\n}\r\n\r\nexport class useMailer {\r\n private transporter: Transporter;\r\n\r\n constructor(private config: MailerConfig) {\r\n this.transporter = createTransport({\r\n host: config.host,\r\n port: config.port,\r\n secure: config.secure,\r\n auth: { user: config.email, pass: config.password },\r\n });\r\n }\r\n\r\n async sendMail({\r\n sender,\r\n to,\r\n subject,\r\n text,\r\n html,\r\n }: {\r\n sender?: string;\r\n to: string;\r\n subject: string;\r\n text?: string;\r\n html?: string;\r\n }): Promise<string> {\r\n const from = sender\r\n ? `${sender} <${this.config.email}>`\r\n : this.config.email;\r\n\r\n const mailOptions: SendMailOptions = {\r\n from,\r\n to,\r\n subject,\r\n ...(text && { text }),\r\n ...(html && { html }),\r\n };\r\n\r\n try {\r\n await this.transporter.sendMail(mailOptions);\r\n logger.info(\r\n `[Mailer][Send] Email sent to \"${to}\" with subject \"${subject}\".`\r\n );\r\n\r\n return \"Mail sent successfully.\";\r\n } catch (error) {\r\n logger.error(\r\n `[Mailer][Send] Failed to send email to \"${to}\" with subject \"${subject}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to send email.\");\r\n }\r\n }\r\n}\r\n","import { ObjectId } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nexport function toObjectId(id: string | ObjectId): ObjectId {\r\n if (!id) {\r\n logger.error(\r\n `[ObjectId][Convert] No value provided for MongoDB ObjectId conversion.`\r\n );\r\n throw new BadRequestError(\r\n \"A value must be provided for ObjectId conversion.\"\r\n );\r\n }\r\n\r\n if (id instanceof ObjectId) return id;\r\n\r\n if (!/^[0-9a-fA-F]{24}$/.test(id)) {\r\n logger.error(\r\n `[ObjectId][Convert] Provided value is not a valid 24-character hex string.`\r\n );\r\n throw new BadRequestError(\r\n \"Invalid ObjectId: must be a 24-character hexadecimal string.\"\r\n );\r\n }\r\n\r\n try {\r\n return new ObjectId(id);\r\n } catch (err) {\r\n logger.error(\r\n `[ObjectId][Convert] Failed to convert value to ObjectId: ${\r\n err instanceof Error ? err.message : err\r\n }`\r\n );\r\n throw new BadRequestError(\"Failed to convert value into a valid ObjectId.\");\r\n }\r\n}\r\n","export function paginate<T>(\r\n items: T[],\r\n page: number = 0,\r\n limit: number = 10,\r\n total: number\r\n) {\r\n if (total === 0 || items.length === 0) {\r\n return {\r\n items: [],\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n\r\n const startIndex = (page - 1) * limit + 1;\r\n if (startIndex > total) {\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n const endIndex = Math.min(startIndex + items.length - 1, total);\r\n\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `${startIndex}-${endIndex} of ${total}`,\r\n };\r\n}\r\n","import bcrypt from \"bcrypt\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_SALT_ROUNDS = 10;\r\n\r\nexport async function comparePasswords(\r\n password: string,\r\n hashed: string\r\n): Promise<boolean> {\r\n try {\r\n return await bcrypt.compare(password, hashed);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Compare] Failed to compare passwords: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n return false;\r\n }\r\n}\r\n\r\nexport async function hashPassword(\r\n password: string,\r\n saltRounds: number = DEFAULT_SALT_ROUNDS\r\n): Promise<string> {\r\n try {\r\n return await bcrypt.hash(password, saltRounds);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Hash] Failed to hash password: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to hash password.\");\r\n }\r\n}\r\n","import { createClient, RedisClientType } from \"redis\";\r\nimport { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from \"./../config\";\r\nimport { logger } from \"./logger\";\r\n\r\nlet redisClient: RedisClientType | null = null;\r\n\r\nexport async function initRedisClient() {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n redisClient = createClient({\r\n password: REDIS_PASSWORD,\r\n socket: {\r\n host: REDIS_HOST,\r\n port: REDIS_PORT,\r\n },\r\n });\r\n}\r\n\r\nexport function useRedisClient(): RedisClientType {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new Error(\r\n \"[Redis][GetClient] Redis connection is not initialized. Call initRedisClient() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n}\r\n","import * as dotenv from \"dotenv\";\r\n\r\ndotenv.config();\r\n\r\nfunction getEnv(key: string, fallback?: string): string {\r\n const value = process.env[key];\r\n if (value !== undefined) return value as string;\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvNumber(key: string, fallback?: number): number {\r\n const value = process.env[key];\r\n if (value !== undefined) return Number(value);\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvBoolean(key: string, fallback?: boolean): boolean {\r\n const value = process.env[key];\r\n if (value !== undefined) return value === \"true\";\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\n// Redis\r\nexport const REDIS_HOST = getEnv(\"REDIS_HOST\");\r\nexport const REDIS_PORT = getEnvNumber(\"REDIS_PORT\", 6379);\r\nexport const REDIS_PASSWORD = getEnv(\"REDIS_PASSWORD\");\r\nexport const REDIS_TLS = getEnvBoolean(\"REDIS_TLS\", true);\r\n","import {\r\n DeleteObjectCommand,\r\n PutObjectCommand,\r\n S3Client,\r\n} from \"@aws-sdk/client-s3\";\r\nimport { Readable } from \"stream\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface S3Config {\r\n accessKeyId: string;\r\n secretAccessKey: string;\r\n endpoint: string;\r\n region: string;\r\n bucket: string;\r\n forcePathStyle: boolean;\r\n}\r\n\r\nexport class useS3 {\r\n private client: S3Client;\r\n\r\n constructor(private config: S3Config) {\r\n this.client = new S3Client({\r\n endpoint: config.endpoint,\r\n region: config.region,\r\n credentials: {\r\n accessKeyId: config.accessKeyId,\r\n secretAccessKey: config.secretAccessKey,\r\n },\r\n forcePathStyle: config.forcePathStyle,\r\n });\r\n }\r\n\r\n async uploadObject({\r\n key,\r\n body,\r\n metadata = {},\r\n contentType,\r\n }: {\r\n key: string;\r\n body: string | Buffer;\r\n metadata?: Record<string, string>;\r\n contentType?: string;\r\n }): Promise<string> {\r\n try {\r\n await this.client.send(\r\n new PutObjectCommand({\r\n Bucket: this.config.bucket,\r\n Key: key,\r\n Body:\r\n typeof body === \"string\" || Buffer.isBuffer(body)\r\n ? body\r\n : Readable.from(body),\r\n ACL: \"public-read\",\r\n Metadata: metadata,\r\n ContentType: contentType,\r\n ContentLength:\r\n typeof body === \"string\" ? Buffer.byteLength(body) : body.length,\r\n })\r\n );\r\n\r\n logger.info(\r\n `[S3][Upload] Uploaded \"${key}\" to bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully uploaded file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Upload][Error] Failed to upload \"${key}\" to bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n async deleteObject(key: string) {\r\n try {\r\n await this.client.send(\r\n new DeleteObjectCommand({ Key: key, Bucket: this.config.bucket })\r\n );\r\n\r\n logger.info(\r\n `[S3][Delete] Deleted \"${key}\" from bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully deleted file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Delete][Error] Failed to delete \"${key}\" from bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n}\r\n","import crypto from \"crypto\";\r\n\r\n// Deterministic hash for tokens at rest (do NOT use bcrypt here)\r\n// Optionally salt via env if provided\r\nconst TOKEN_HASH_SALT = process.env.REFRESH_TOKEN_HASH_SALT || \"\";\r\n\r\nexport function hashToken(token: string): string {\r\n return crypto\r\n .createHash(\"sha256\")\r\n .update(TOKEN_HASH_SALT + token)\r\n .digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":["transports","config","redisClient","jwt","jwt","config","redisClient","config"]}
|
|
1
|
+
{"version":3,"sources":["../src/middleware/auth.middleware.ts","../src/utils/logger.ts","../src/utils/http-error.ts","../src/middleware/error-handler.middleware.ts","../src/utils/atlas.ts","../src/utils/ioredis.ts","../src/utils/cache.ts","../src/utils/cache-key.ts","../src/utils/get-template-path.ts","../src/utils/handlebars-compiler.ts","../src/utils/jwt.ts","../src/utils/mailer.ts","../src/utils/objectid-converter.ts","../src/utils/paginate.ts","../src/utils/password.ts","../src/utils/redis.ts","../src/config.ts","../src/utils/s3.ts","../src/utils/token.ts"],"sourcesContent":["import { NextFunction, Request, Response } from \"express\";\r\nimport jwt, { JwtPayload } from \"jsonwebtoken\";\r\nimport { logger } from \"./../utils/logger\";\r\nimport { UnauthorizedError, ForbiddenError } from \"./../utils/http-error\";\r\n\r\ninterface DecodedToken extends JwtPayload {\r\n user?: string;\r\n id?: string;\r\n jti?: string;\r\n role?: string;\r\n}\r\n\r\nexport interface AuthenticatedRequest extends Request {\r\n user?: DecodedToken & { id: string };\r\n token?: string;\r\n}\r\n\r\ninterface AuthOptions {\r\n secretKey?: string;\r\n isTokenRevoked?: (jti: string) => Promise<boolean>;\r\n requiredRole?: string;\r\n}\r\n\r\n// Helper function to extract token from Authorization header\r\nfunction extractToken(authHeader?: string): string | null {\r\n logger.debug(\"Attempting to extract token from Authorization header\");\r\n\r\n if (!authHeader) {\r\n logger.debug(\"No Authorization header provided\");\r\n return null;\r\n }\r\n\r\n if (!authHeader.startsWith(\"Bearer \")) {\r\n logger.warn(\"Authorization header does not start with 'Bearer '\", {\r\n authHeader: authHeader.substring(0, 20) + \"...\",\r\n });\r\n return null;\r\n }\r\n\r\n const token = authHeader.split(\" \")[1] || null;\r\n logger.debug(\"Token extraction result\", {\r\n hasToken: !!token,\r\n tokenLength: token?.length,\r\n });\r\n return token;\r\n}\r\n\r\n// Core authentication logic - DRY principle\r\nasync function authenticateToken(\r\n req: AuthenticatedRequest,\r\n options: AuthOptions\r\n): Promise<void> {\r\n const {\r\n secretKey = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked,\r\n requiredRole,\r\n } = options;\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n\r\n logger.info(\"Starting authentication process\", {\r\n requestId,\r\n path: req.path,\r\n method: req.method,\r\n requiredRole,\r\n hasRevocationCheck: !!isTokenRevoked,\r\n });\r\n\r\n const token = extractToken(req.headers.authorization);\r\n if (!token) {\r\n logger.error(\"Authentication failed: No access token provided\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n throw new UnauthorizedError(\"Access token is required to proceed.\");\r\n }\r\n\r\n logger.debug(\"Token extracted successfully, proceeding with verification\", {\r\n requestId,\r\n tokenLength: token.length,\r\n });\r\n\r\n let decoded: DecodedToken;\r\n try {\r\n logger.debug(\"Verifying JWT token\", { requestId });\r\n decoded = jwt.verify(token, secretKey) as DecodedToken;\r\n logger.info(\"JWT token verified successfully\", {\r\n requestId,\r\n userId: decoded.user || decoded.id,\r\n role: decoded.role,\r\n jti: decoded.jti,\r\n exp: decoded.exp ? new Date(decoded.exp * 1000).toISOString() : \"unknown\",\r\n });\r\n } catch (error) {\r\n logger.error(\"JWT verification failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n tokenLength: token.length,\r\n });\r\n throw new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n );\r\n }\r\n\r\n // Check if token is revoked\r\n if (isTokenRevoked && decoded.jti) {\r\n logger.debug(\"Checking token revocation status\", {\r\n requestId,\r\n jti: decoded.jti,\r\n });\r\n const isRevoked = await isTokenRevoked(decoded.jti);\r\n if (isRevoked) {\r\n logger.warn(\"Authentication failed: Token is revoked\", {\r\n requestId,\r\n jti: decoded.jti,\r\n userId: decoded.user || decoded.id,\r\n });\r\n throw new UnauthorizedError(\r\n \"Your session has expired or the token is invalid. Please log in again.\"\r\n );\r\n }\r\n logger.debug(\"Token revocation check passed\", {\r\n requestId,\r\n jti: decoded.jti,\r\n });\r\n }\r\n\r\n // Check role requirements\r\n if (requiredRole && decoded.role !== requiredRole) {\r\n logger.warn(\"Authorization failed: Insufficient permissions\", {\r\n requestId,\r\n userId: decoded.user || decoded.id,\r\n userRole: decoded.role,\r\n requiredRole,\r\n path: req.path,\r\n });\r\n throw new ForbiddenError(\r\n \"Insufficient permissions to access this resource.\"\r\n );\r\n }\r\n\r\n // Set user data on request\r\n req.user = {\r\n ...decoded,\r\n id: decoded.user || decoded.id || \"\",\r\n };\r\n req.token = token;\r\n\r\n logger.info(\"Authentication completed successfully\", {\r\n requestId,\r\n userId: req.user.id,\r\n role: req.user.role,\r\n path: req.path,\r\n method: req.method,\r\n });\r\n}\r\n\r\nexport function authenticate(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"Authenticate middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, { secretKey, isTokenRevoked });\r\n logger.debug(\"Authenticate middleware completed successfully\", {\r\n requestId,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"Authenticate middleware failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n\r\nexport function requireAdmin(\r\n secretKey: string = process.env.ACCESS_TOKEN_SECRET || \"\",\r\n isTokenRevoked?: (jti: string) => Promise<boolean>\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"RequireAdmin middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, {\r\n secretKey,\r\n isTokenRevoked,\r\n requiredRole: \"admin\",\r\n });\r\n logger.debug(\"RequireAdmin middleware completed successfully\", {\r\n requestId,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"RequireAdmin middleware failed\", {\r\n requestId,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n\r\n// Additional helper middleware for role-based access\r\nexport function requireRole(\r\n role: string,\r\n options?: Omit<AuthOptions, \"requiredRole\">\r\n) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction\r\n ) => {\r\n const requestId = req.headers[\"x-request-id\"] || \"unknown\";\r\n logger.debug(\"RequireRole middleware invoked\", {\r\n requestId,\r\n path: req.path,\r\n requiredRole: role,\r\n });\r\n\r\n try {\r\n await authenticateToken(req, {\r\n ...options,\r\n requiredRole: role,\r\n });\r\n logger.debug(\"RequireRole middleware completed successfully\", {\r\n requestId,\r\n role,\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error(\"RequireRole middleware failed\", {\r\n requestId,\r\n requiredRole: role,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n });\r\n next(error);\r\n }\r\n };\r\n}\r\n","import * as winston from \"winston\";\r\n\r\nconst transports = [\r\n new winston.transports.File({ filename: \"error.log\", level: \"error\" }),\r\n new winston.transports.File({ filename: \"combined.log\" }),\r\n];\r\n\r\nexport const logger = winston.createLogger({\r\n level: \"info\",\r\n format: winston.format.combine(\r\n winston.format.timestamp(),\r\n winston.format.json()\r\n ),\r\n transports,\r\n});\r\n","export class HttpError extends Error {\r\n public readonly statusCode: number;\r\n public readonly isOperational: boolean;\r\n\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n isOperational: boolean = true\r\n ) {\r\n super(message);\r\n\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n\r\n this.statusCode = statusCode;\r\n this.isOperational = isOperational;\r\n\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, this.constructor);\r\n }\r\n }\r\n}\r\n\r\nexport class BadRequestError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be processed. Please review your input and try again.\"\r\n ) {\r\n super(message, 400, true);\r\n }\r\n}\r\n\r\nexport class UnauthorizedError extends HttpError {\r\n constructor(\r\n message: string = \"Authentication is required to access this resource.\"\r\n ) {\r\n super(message, 401, true);\r\n }\r\n}\r\n\r\nexport class ForbiddenError extends HttpError {\r\n constructor(\r\n message: string = \"You do not have the necessary permissions to perform this action.\"\r\n ) {\r\n super(message, 403, true);\r\n }\r\n}\r\n\r\nexport class NotFoundError extends HttpError {\r\n constructor(message: string = \"The requested resource could not be found.\") {\r\n super(message, 404, true);\r\n }\r\n}\r\n\r\nexport class ConflictError extends HttpError {\r\n constructor(\r\n message: string = \"A resource with the provided values already exists.\"\r\n ) {\r\n super(message, 409, true);\r\n }\r\n}\r\n\r\nexport class UnprocessableEntityError extends HttpError {\r\n constructor(\r\n message: string = \"The request could not be completed due to invalid or incomplete information.\"\r\n ) {\r\n super(message, 422, true);\r\n }\r\n}\r\n\r\nexport class InternalServerError extends HttpError {\r\n constructor(\r\n message: string = \"An internal server error occurred. Please try again later.\"\r\n ) {\r\n super(message, 500, true);\r\n }\r\n}\r\n","import { NextFunction, Request, Response } from \"express\";\r\nimport { HttpError, InternalServerError } from \"./../utils/http-error\";\r\nimport { logger } from \"./../utils/logger\";\r\n\r\nexport const errorHandler = (\r\n error: HttpError,\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n) => {\r\n if (error.isOperational) {\r\n res\r\n .status(error.statusCode)\r\n .json({ status: \"error\", message: error.message });\r\n } else {\r\n logger.error({ message: error.message });\r\n res\r\n .status(500)\r\n .json({ status: \"error\", message: new InternalServerError().message });\r\n }\r\n\r\n return;\r\n};\r\n","import { Db, MongoClient } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport {\r\n BadRequestError,\r\n InternalServerError,\r\n NotFoundError,\r\n} from \"./http-error\";\r\n\r\ninterface AtlasConfig {\r\n uri: string;\r\n db: string;\r\n name?: string;\r\n}\r\n\r\nexport class useAtlas {\r\n private static mongoClient: MongoClient | null = null;\r\n private static mongoDb: Db | null = null;\r\n\r\n public static async connect(config: AtlasConfig): Promise<void> {\r\n if (this.mongoClient) {\r\n logger.warn(\r\n \"[MongoDB][Connect] Client is already connected. Skipping new connection.\"\r\n );\r\n throw new BadRequestError(\"A MongoDB connection is already established.\");\r\n }\r\n\r\n const { db, uri } = config;\r\n this.mongoClient = new MongoClient(uri, {\r\n maxPoolSize: 10,\r\n maxIdleTimeMS: 60000,\r\n connectTimeoutMS: 60000,\r\n });\r\n\r\n try {\r\n await this.mongoClient.connect();\r\n this.mongoDb = this.mongoClient.db(db);\r\n\r\n logger.info(\r\n `[MongoDB][Connect] Connected to database \"${this.mongoDb.databaseName}\".`\r\n );\r\n } catch (error) {\r\n this.mongoClient = null;\r\n\r\n logger.error(\r\n `[MongoDB][Connect] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\r\n \"Failed to connect to the database. Please try again later.\"\r\n );\r\n }\r\n }\r\n\r\n public static getClient(): MongoClient | null {\r\n if (!this.mongoClient) {\r\n logger.warn(`[MongoDB][GetClient] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return this.mongoClient;\r\n }\r\n\r\n public static getDb(): Db | null {\r\n if (!this.mongoDb) {\r\n logger.warn(`[MongoDB][GetDb] Database instance is not available.`);\r\n throw new NotFoundError(\"MongoDB database instance is not available.\");\r\n }\r\n\r\n return this.mongoDb;\r\n }\r\n\r\n public static startSession(): import(\"mongodb\").ClientSession {\r\n const client = this.getClient();\r\n\r\n if (!client) {\r\n logger.warn(`[MongoDB][StartSession] Client is not initialized.`);\r\n throw new NotFoundError(\"MongoDB client is not initialized.\");\r\n }\r\n\r\n return client.startSession();\r\n }\r\n\r\n public static async close(): Promise<void> {\r\n if (this.mongoClient) {\r\n try {\r\n await this.mongoClient.close();\r\n\r\n logger.info(`[MongoDB][Close] Connection closed.`);\r\n } catch (error) {\r\n logger.error(\r\n `[MongoDB][Close] Error closing connection: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to close MongoDB connection.\");\r\n } finally {\r\n this.mongoClient = null;\r\n this.mongoDb = null;\r\n }\r\n } else {\r\n logger.warn(`[MongoDB][Close] No client to disconnect.`);\r\n throw new BadRequestError(\"No MongoDB connection exists to close.\");\r\n }\r\n }\r\n}\r\n","import Redis from \"ioredis\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nlet redisClient: Redis | null = null;\r\n\r\ntype RedisOptions = {\r\n host?: string;\r\n port?: number;\r\n username?: string;\r\n password?: string;\r\n};\r\n\r\nexport function useRedis() {\r\n function initialize(options: RedisOptions) {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n options.host = options.host ?? \"localhost\";\r\n options.port = options.port ?? 6379;\r\n options.username = options.username ?? \"default\";\r\n options.password = options.password ?? \"\";\r\n\r\n redisClient = new Redis({\r\n host: options.host,\r\n port: options.port,\r\n ...(options.username && { username: options.username }),\r\n ...(options.password && { password: options.password }),\r\n });\r\n\r\n redisClient.on(\"connect\", () => {\r\n logger.info(\r\n `[Redis][Connect] Redis client connected at ${options.host}:${options.port}.`\r\n );\r\n });\r\n\r\n redisClient.on(\"error\", (error) => {\r\n logger.error(\r\n `[Redis][Error] Failed to connect: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n });\r\n\r\n return redisClient;\r\n }\r\n\r\n function getClient(): Redis {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new BadRequestError(\r\n \"Redis connection is not initialized. Please call initialize() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n }\r\n\r\n function disconnect() {\r\n if (redisClient) {\r\n redisClient.quit();\r\n logger.info(\"[Redis][Disconnect] Redis connection has been closed.\");\r\n redisClient = null;\r\n } else {\r\n logger.warn(\"[Redis][Disconnect] No Redis client to disconnect.\");\r\n }\r\n }\r\n\r\n return {\r\n initialize,\r\n getClient,\r\n disconnect,\r\n };\r\n}\r\n","import { useRedis } from \"./ioredis\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_TTL = 300;\r\n\r\nexport function useCache() {\r\n function getRedisClient() {\r\n return useRedis().getClient();\r\n }\r\n\r\n async function get<T = unknown>(cacheKey: string): Promise<T | null> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const value = await redisClient.get(cacheKey);\r\n\r\n return value ? (JSON.parse(value) as T) : null;\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Get] Failed to retrieve key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n\r\n return null;\r\n }\r\n }\r\n\r\n async function set<T = unknown>(\r\n cacheKey: string,\r\n data: T,\r\n ttl: number = DEFAULT_TTL,\r\n group?: string\r\n ): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.set(cacheKey, JSON.stringify(data), \"EX\", ttl);\r\n logger.info(`[Cache][Set] Stored key \"${cacheKey}\" with TTL ${ttl}s.`);\r\n\r\n if (group) await redisClient.sadd(`cache:group:${group}`, cacheKey);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Set] Failed to store key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function remove(cacheKey: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n await redisClient.del(cacheKey);\r\n logger.info(`[Cache][Remove] Key \"${cacheKey}\" has been deleted.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][Remove] Failed to delete key \"${cacheKey}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n async function clearGroup(group: string): Promise<void> {\r\n try {\r\n const redisClient = getRedisClient();\r\n const keys = await redisClient.smembers(`cache:group:${group}`);\r\n if (keys.length) await redisClient.del(...keys);\r\n\r\n await redisClient.del(`cache:group:${group}`);\r\n logger.info(`[Cache][ClearGroup] Cleared group \"${group}\" and its keys.`);\r\n } catch (error) {\r\n logger.error(\r\n `[Cache][ClearGroup] Failed to clear group \"${group}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n }\r\n }\r\n\r\n return {\r\n get,\r\n set,\r\n remove,\r\n clearGroup,\r\n };\r\n}\r\n","export function buildCacheKey(\r\n prefix: string,\r\n values: Record<string, any>\r\n): string {\r\n const query = Object.entries(values)\r\n .sort(([a], [b]) => a.localeCompare(b))\r\n .map(\r\n ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`\r\n )\r\n .join(\"&\");\r\n\r\n return `${prefix}:${query}`;\r\n}\r\n","import path from \"path\";\r\n\r\nexport function getTemplatePath(directory: string, filePath: string) {\r\n const ext = \".hbs\";\r\n const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;\r\n\r\n // Resolve from project root, assuming templates are in src/public/templates or public/templates\r\n const possiblePaths = [\r\n path.join(process.cwd(), \"src\", \"public\", directory, file),\r\n path.join(process.cwd(), \"public\", directory, file),\r\n path.join(process.cwd(), directory, file),\r\n ];\r\n\r\n // Return the first path that exists, or default to src/public structure\r\n const fs = require(\"fs\");\r\n for (const templatePath of possiblePaths) {\r\n if (fs.existsSync(templatePath)) {\r\n return templatePath;\r\n }\r\n }\r\n\r\n // Default to src/public structure if none exist\r\n return possiblePaths[0];\r\n}\r\n","import Handlebars from \"handlebars\";\r\nimport { promises as fs } from \"fs\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface CompileOptions {\r\n context?: Record<string, any>;\r\n filePath: string;\r\n}\r\n\r\nconst handlebarsTemplateCache = new Map<string, Handlebars.TemplateDelegate>();\r\n\r\n// Register Handlebars helpers\r\nHandlebars.registerHelper(\"formatCurrency\", function (amount: number) {\r\n if (typeof amount !== \"number\") return \"0.00\";\r\n return new Intl.NumberFormat(\"en-PH\", {\r\n style: \"decimal\",\r\n minimumFractionDigits: 2,\r\n maximumFractionDigits: 2,\r\n }).format(amount);\r\n});\r\n\r\nHandlebars.registerHelper(\r\n \"formatDate\",\r\n function (date: Date | string, format: string) {\r\n if (!date) return \"N/A\";\r\n\r\n const dateObj = typeof date === \"string\" ? new Date(date) : date;\r\n if (isNaN(dateObj.getTime())) return \"N/A\";\r\n\r\n const options: Intl.DateTimeFormatOptions = {};\r\n\r\n if (format.includes(\"MMMM\")) options.month = \"long\";\r\n else if (format.includes(\"MMM\")) options.month = \"short\";\r\n else if (format.includes(\"MM\")) options.month = \"2-digit\";\r\n\r\n if (format.includes(\"D,\")) options.day = \"numeric\";\r\n else if (format.includes(\"DD\")) options.day = \"2-digit\";\r\n\r\n if (format.includes(\"YYYY\")) options.year = \"numeric\";\r\n else if (format.includes(\"YY\")) options.year = \"2-digit\";\r\n\r\n if (format.includes(\"h:mm A\")) {\r\n options.hour = \"numeric\";\r\n options.minute = \"2-digit\";\r\n options.hour12 = true;\r\n } else if (format.includes(\"HH:mm\")) {\r\n options.hour = \"2-digit\";\r\n options.minute = \"2-digit\";\r\n options.hour12 = false;\r\n }\r\n\r\n return new Intl.DateTimeFormat(\"en-US\", options).format(dateObj);\r\n }\r\n);\r\n\r\nexport async function renderHandlebarsTemplate({\r\n context = {},\r\n filePath,\r\n}: CompileOptions): Promise<string> {\r\n try {\r\n let compiledTemplate = handlebarsTemplateCache.get(filePath);\r\n\r\n if (!compiledTemplate) {\r\n logger.info(\r\n `[Template][Compile] Compiling and caching template from \"${filePath}\".`\r\n );\r\n const fileContent = await fs.readFile(filePath, \"utf8\");\r\n compiledTemplate = Handlebars.compile(fileContent);\r\n handlebarsTemplateCache.set(filePath, compiledTemplate);\r\n } else {\r\n logger.info(`[Template][Cache] Using cached template for \"${filePath}\".`);\r\n }\r\n\r\n return compiledTemplate(context);\r\n } catch (error) {\r\n logger.error(\r\n `[Template][Render] Failed to render template \"${filePath}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to render Handlebars template.\");\r\n }\r\n}\r\n","import jwt from \"jsonwebtoken\";\r\nimport { BadRequestError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface JwtSignParams {\r\n payload?: Record<string, unknown>;\r\n secretKey: string;\r\n signOptions?: jwt.SignOptions;\r\n}\r\n\r\nexport function signJwtToken({\r\n payload = {},\r\n secretKey = \"\",\r\n signOptions = {},\r\n}: JwtSignParams): string {\r\n if (!secretKey) {\r\n logger.error(`[JWT][Sign] Secret key is missing. Cannot sign token.`);\r\n throw new BadRequestError(\"A JWT secret key must be provided.\");\r\n }\r\n\r\n try {\r\n logger.info(`[JWT][Sign] Signing JWT token.`);\r\n return jwt.sign(payload, secretKey, signOptions);\r\n } catch (error) {\r\n logger.error(\r\n `[JWT][Sign] Failed to sign JWT token: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw error;\r\n }\r\n}\r\n","import { createTransport, SendMailOptions, Transporter } from \"nodemailer\";\r\nimport { logger } from \"./logger\";\r\nimport { InternalServerError } from \"./http-error\";\r\n\r\ninterface MailerConfig {\r\n email: string;\r\n password: string;\r\n host: string;\r\n port: number;\r\n secure: boolean;\r\n}\r\n\r\nexport class useMailer {\r\n private transporter: Transporter;\r\n\r\n constructor(private config: MailerConfig) {\r\n this.transporter = createTransport({\r\n host: config.host,\r\n port: config.port,\r\n secure: config.secure,\r\n auth: { user: config.email, pass: config.password },\r\n });\r\n }\r\n\r\n async sendMail({\r\n sender,\r\n to,\r\n subject,\r\n text,\r\n html,\r\n }: {\r\n sender?: string;\r\n to: string;\r\n subject: string;\r\n text?: string;\r\n html?: string;\r\n }): Promise<string> {\r\n const from = sender\r\n ? `${sender} <${this.config.email}>`\r\n : this.config.email;\r\n\r\n const mailOptions: SendMailOptions = {\r\n from,\r\n to,\r\n subject,\r\n ...(text && { text }),\r\n ...(html && { html }),\r\n };\r\n\r\n try {\r\n await this.transporter.sendMail(mailOptions);\r\n logger.info(\r\n `[Mailer][Send] Email sent to \"${to}\" with subject \"${subject}\".`\r\n );\r\n\r\n return \"Mail sent successfully.\";\r\n } catch (error) {\r\n logger.error(\r\n `[Mailer][Send] Failed to send email to \"${to}\" with subject \"${subject}\": ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to send email.\");\r\n }\r\n }\r\n}\r\n","import { ObjectId } from \"mongodb\";\r\nimport { logger } from \"./logger\";\r\nimport { BadRequestError } from \"./http-error\";\r\n\r\nexport function toObjectId(id: string | ObjectId): ObjectId {\r\n if (!id) {\r\n logger.error(\r\n `[ObjectId][Convert] No value provided for MongoDB ObjectId conversion.`\r\n );\r\n throw new BadRequestError(\r\n \"A value must be provided for ObjectId conversion.\"\r\n );\r\n }\r\n\r\n if (id instanceof ObjectId) return id;\r\n\r\n if (!/^[0-9a-fA-F]{24}$/.test(id)) {\r\n logger.error(\r\n `[ObjectId][Convert] Provided value is not a valid 24-character hex string.`\r\n );\r\n throw new BadRequestError(\r\n \"Invalid ObjectId: must be a 24-character hexadecimal string.\"\r\n );\r\n }\r\n\r\n try {\r\n return new ObjectId(id);\r\n } catch (err) {\r\n logger.error(\r\n `[ObjectId][Convert] Failed to convert value to ObjectId: ${\r\n err instanceof Error ? err.message : err\r\n }`\r\n );\r\n throw new BadRequestError(\"Failed to convert value into a valid ObjectId.\");\r\n }\r\n}\r\n","export function paginate<T>(\r\n items: T[],\r\n page: number = 0,\r\n limit: number = 10,\r\n total: number\r\n) {\r\n if (total === 0 || items.length === 0) {\r\n return {\r\n items: [],\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n\r\n const startIndex = (page - 1) * limit + 1;\r\n if (startIndex > total) {\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `0-0 of ${total}`,\r\n };\r\n }\r\n const endIndex = Math.min(startIndex + items.length - 1, total);\r\n\r\n return {\r\n items,\r\n pages: Math.ceil(total / limit),\r\n pageRange: `${startIndex}-${endIndex} of ${total}`,\r\n };\r\n}\r\n","import bcrypt from \"bcrypt\";\r\nimport { InternalServerError } from \"./http-error\";\r\nimport { logger } from \"./logger\";\r\n\r\nconst DEFAULT_SALT_ROUNDS = 10;\r\n\r\nexport async function comparePasswords(\r\n password: string,\r\n hashed: string\r\n): Promise<boolean> {\r\n try {\r\n return await bcrypt.compare(password, hashed);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Compare] Failed to compare passwords: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n return false;\r\n }\r\n}\r\n\r\nexport async function hashPassword(\r\n password: string,\r\n saltRounds: number = DEFAULT_SALT_ROUNDS\r\n): Promise<string> {\r\n try {\r\n return await bcrypt.hash(password, saltRounds);\r\n } catch (error) {\r\n logger.error(\r\n `[Password][Hash] Failed to hash password: ${\r\n error instanceof Error ? error.message : error\r\n }`\r\n );\r\n throw new InternalServerError(\"Failed to hash password.\");\r\n }\r\n}\r\n","import { createClient, RedisClientType } from \"redis\";\r\nimport { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from \"./../config\";\r\nimport { logger } from \"./logger\";\r\n\r\nlet redisClient: RedisClientType | null = null;\r\n\r\nexport async function initRedisClient() {\r\n if (redisClient) {\r\n logger.info(\"[Redis][Init] Redis connection is already established.\");\r\n return redisClient;\r\n }\r\n\r\n redisClient = createClient({\r\n password: REDIS_PASSWORD,\r\n socket: {\r\n host: REDIS_HOST,\r\n port: REDIS_PORT,\r\n },\r\n });\r\n}\r\n\r\nexport function useRedisClient(): RedisClientType {\r\n if (!redisClient) {\r\n logger.error(\r\n \"[Redis][GetClient] Redis connection has not been initialized.\"\r\n );\r\n throw new Error(\r\n \"[Redis][GetClient] Redis connection is not initialized. Call initRedisClient() first.\"\r\n );\r\n }\r\n\r\n return redisClient;\r\n}\r\n","import * as dotenv from \"dotenv\";\r\n\r\ndotenv.config();\r\n\r\nfunction getEnv(key: string, fallback?: string): string {\r\n const value = process.env[key];\r\n if (value !== undefined) return value as string;\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvNumber(key: string, fallback?: number): number {\r\n const value = process.env[key];\r\n if (value !== undefined) return Number(value);\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\nfunction getEnvBoolean(key: string, fallback?: boolean): boolean {\r\n const value = process.env[key];\r\n if (value !== undefined) return value === \"true\";\r\n if (fallback !== undefined) return fallback;\r\n throw new Error(`Missing required environment variable: ${key}`);\r\n}\r\n\r\n// Redis\r\nexport const REDIS_HOST = getEnv(\"REDIS_HOST\");\r\nexport const REDIS_PORT = getEnvNumber(\"REDIS_PORT\", 6379);\r\nexport const REDIS_PASSWORD = getEnv(\"REDIS_PASSWORD\");\r\nexport const REDIS_TLS = getEnvBoolean(\"REDIS_TLS\", true);\r\n","import {\r\n DeleteObjectCommand,\r\n PutObjectCommand,\r\n S3Client,\r\n} from \"@aws-sdk/client-s3\";\r\nimport { Readable } from \"stream\";\r\nimport { logger } from \"./logger\";\r\n\r\ninterface S3Config {\r\n accessKeyId: string;\r\n secretAccessKey: string;\r\n endpoint: string;\r\n region: string;\r\n bucket: string;\r\n forcePathStyle: boolean;\r\n}\r\n\r\nexport class useS3 {\r\n private client: S3Client;\r\n\r\n constructor(private config: S3Config) {\r\n this.client = new S3Client({\r\n endpoint: config.endpoint,\r\n region: config.region,\r\n credentials: {\r\n accessKeyId: config.accessKeyId,\r\n secretAccessKey: config.secretAccessKey,\r\n },\r\n forcePathStyle: config.forcePathStyle,\r\n });\r\n }\r\n\r\n async uploadObject({\r\n key,\r\n body,\r\n metadata = {},\r\n contentType,\r\n }: {\r\n key: string;\r\n body: string | Buffer;\r\n metadata?: Record<string, string>;\r\n contentType?: string;\r\n }): Promise<string> {\r\n try {\r\n await this.client.send(\r\n new PutObjectCommand({\r\n Bucket: this.config.bucket,\r\n Key: key,\r\n Body:\r\n typeof body === \"string\" || Buffer.isBuffer(body)\r\n ? body\r\n : Readable.from(body),\r\n ACL: \"public-read\",\r\n Metadata: metadata,\r\n ContentType: contentType,\r\n ContentLength:\r\n typeof body === \"string\" ? Buffer.byteLength(body) : body.length,\r\n })\r\n );\r\n\r\n logger.info(\r\n `[S3][Upload] Uploaded \"${key}\" to bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully uploaded file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Upload][Error] Failed to upload \"${key}\" to bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n async deleteObject(key: string) {\r\n try {\r\n await this.client.send(\r\n new DeleteObjectCommand({ Key: key, Bucket: this.config.bucket })\r\n );\r\n\r\n logger.info(\r\n `[S3][Delete] Deleted \"${key}\" from bucket \"${this.config.bucket}\".`\r\n );\r\n return \"Successfully deleted file.\";\r\n } catch (error) {\r\n logger.error(\r\n `[S3][Delete][Error] Failed to delete \"${key}\" from bucket \"${\r\n this.config.bucket\r\n }\": ${error instanceof Error ? error.message : error}`\r\n );\r\n throw error;\r\n }\r\n }\r\n}\r\n","import crypto from \"crypto\";\r\n\r\n// Deterministic hash for tokens at rest (do NOT use bcrypt here)\r\n// Optionally salt via env if provided\r\nconst TOKEN_HASH_SALT = process.env.REFRESH_TOKEN_HASH_SALT || \"\";\r\n\r\nexport function hashToken(token: string): string {\r\n return crypto\r\n .createHash(\"sha256\")\r\n .update(TOKEN_HASH_SALT + token)\r\n .digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":["transports","config","redisClient","fs","format","jwt","jwt","config","redisClient","config"]}
|