@codisolutions23/node-utils 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/shiny-donkeys-glow.md +11 -0
- package/CHANGELOG.md +11 -0
- package/dist/index.d.mts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +219 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +223 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
### Minor Changes
|
|
2
|
+
|
|
3
|
+
- **Cache System Improvements**: Enhanced cache utility with clearer function names (`getCache`, `setCache`, `delCache`, `delCacheGroup`) and configurable TTL settings via `CACHE_SHORT_TTL` and `CACHE_LONG_TTL` environment variables
|
|
4
|
+
|
|
5
|
+
- **Enhanced Error Handling**: Replaced generic Error with NotFoundError for missing environment variables, providing better error specificity and handling
|
|
6
|
+
|
|
7
|
+
- **Reduced Auth Logging**: Optimized authentication middleware logging to reduce production noise while maintaining debug capabilities in development
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- **Dependencies**: Updated AWS SDK packages (@aws-sdk, @smithy) to latest versions (3.908.0, 4.x) for improved security and compatibility
|
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;
|
|
@@ -60,10 +72,10 @@ declare class useAtlas {
|
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
declare function useCache(): {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
getCache: <T = unknown>(cacheKey: string) => Promise<T | null>;
|
|
76
|
+
setCache: <T = unknown>(cacheKey: string, data: T, ttl?: number, group?: string) => Promise<void>;
|
|
77
|
+
delCache: (cacheKey: string) => Promise<void>;
|
|
78
|
+
delCacheGroup: (group: string) => Promise<void>;
|
|
67
79
|
};
|
|
68
80
|
|
|
69
81
|
declare function buildCacheKey(prefix: string, values: Record<string, any>): string;
|
|
@@ -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;
|
|
@@ -60,10 +72,10 @@ declare class useAtlas {
|
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
declare function useCache(): {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
getCache: <T = unknown>(cacheKey: string) => Promise<T | null>;
|
|
76
|
+
setCache: <T = unknown>(cacheKey: string, data: T, ttl?: number, group?: string) => Promise<void>;
|
|
77
|
+
delCache: (cacheKey: string) => Promise<void>;
|
|
78
|
+
delCacheGroup: (group: string) => Promise<void>;
|
|
67
79
|
};
|
|
68
80
|
|
|
69
81
|
declare function buildCacheKey(prefix: string, values: Record<string, any>): string;
|
|
@@ -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,143 @@ 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
|
+
if (!authHeader) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (!authHeader.startsWith("Bearer ")) {
|
|
174
|
+
logger.warn("Invalid Authorization header format", {
|
|
175
|
+
format: "Expected 'Bearer <token>'"
|
|
176
|
+
});
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return authHeader.split(" ")[1] || null;
|
|
180
|
+
}
|
|
181
|
+
function authenticateToken(req, options) {
|
|
182
|
+
return __async(this, null, function* () {
|
|
183
|
+
const {
|
|
184
|
+
secretKey = process.env.ACCESS_TOKEN_SECRET || "",
|
|
185
|
+
isTokenRevoked,
|
|
186
|
+
requiredRole
|
|
187
|
+
} = options;
|
|
188
|
+
const requestId = req.headers["x-request-id"] || "unknown";
|
|
189
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
190
|
+
if (isDev) {
|
|
191
|
+
logger.debug("Starting authentication process", {
|
|
192
|
+
requestId,
|
|
193
|
+
path: req.path,
|
|
194
|
+
requiredRole
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const token = extractToken(req.headers.authorization);
|
|
171
198
|
if (!token) {
|
|
172
|
-
logger.error("No access token provided
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
);
|
|
199
|
+
logger.error("Authentication failed: No access token provided", {
|
|
200
|
+
requestId,
|
|
201
|
+
path: req.path
|
|
202
|
+
});
|
|
203
|
+
throw new UnauthorizedError("Access token is required to proceed.");
|
|
176
204
|
}
|
|
205
|
+
let decoded;
|
|
177
206
|
try {
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
logger.
|
|
181
|
-
|
|
207
|
+
decoded = import_jsonwebtoken.default.verify(token, secretKey);
|
|
208
|
+
if (isDev) {
|
|
209
|
+
logger.debug("JWT token verified", {
|
|
210
|
+
requestId,
|
|
211
|
+
userId: decoded.user || decoded.id,
|
|
212
|
+
role: decoded.role
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.error("JWT verification failed", {
|
|
217
|
+
requestId,
|
|
218
|
+
error: error instanceof Error ? error.message : "Invalid token",
|
|
219
|
+
path: req.path
|
|
220
|
+
});
|
|
221
|
+
throw new UnauthorizedError(
|
|
222
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (isTokenRevoked && decoded.jti) {
|
|
226
|
+
const isRevoked = yield isTokenRevoked(decoded.jti);
|
|
227
|
+
if (isRevoked) {
|
|
228
|
+
logger.warn("Authentication failed: Token is revoked", {
|
|
229
|
+
requestId,
|
|
230
|
+
jti: decoded.jti,
|
|
231
|
+
userId: decoded.user || decoded.id
|
|
182
232
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"Your session has expired or the token is invalid. Please log in again."
|
|
186
|
-
)
|
|
233
|
+
throw new UnauthorizedError(
|
|
234
|
+
"Your session has expired or the token is invalid. Please log in again."
|
|
187
235
|
);
|
|
188
236
|
}
|
|
189
|
-
|
|
190
|
-
|
|
237
|
+
}
|
|
238
|
+
if (requiredRole && decoded.role !== requiredRole) {
|
|
239
|
+
logger.warn("Authorization failed: Insufficient permissions", {
|
|
240
|
+
requestId,
|
|
241
|
+
userId: decoded.user || decoded.id,
|
|
242
|
+
userRole: decoded.role,
|
|
243
|
+
requiredRole,
|
|
244
|
+
path: req.path
|
|
245
|
+
});
|
|
246
|
+
throw new ForbiddenError(
|
|
247
|
+
"Insufficient permissions to access this resource."
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
req.user = __spreadProps(__spreadValues({}, decoded), {
|
|
251
|
+
id: decoded.user || decoded.id || ""
|
|
252
|
+
});
|
|
253
|
+
req.token = token;
|
|
254
|
+
if (isDev) {
|
|
255
|
+
logger.debug("Authentication completed successfully", {
|
|
256
|
+
requestId,
|
|
257
|
+
userId: req.user.id,
|
|
258
|
+
role: req.user.role
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function authenticate(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
264
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
265
|
+
try {
|
|
266
|
+
yield authenticateToken(req, { secretKey, isTokenRevoked });
|
|
267
|
+
next();
|
|
268
|
+
} catch (error) {
|
|
269
|
+
logger.error("Authenticate middleware failed", {
|
|
270
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
271
|
+
});
|
|
272
|
+
next(error);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function requireAdmin(secretKey = process.env.ACCESS_TOKEN_SECRET || "", isTokenRevoked) {
|
|
277
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
278
|
+
try {
|
|
279
|
+
yield authenticateToken(req, {
|
|
280
|
+
secretKey,
|
|
281
|
+
isTokenRevoked,
|
|
282
|
+
requiredRole: "admin"
|
|
191
283
|
});
|
|
192
|
-
req.token = token;
|
|
193
284
|
next();
|
|
194
285
|
} catch (error) {
|
|
195
|
-
logger.error("
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
286
|
+
logger.error("RequireAdmin middleware failed", {
|
|
287
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
288
|
+
});
|
|
289
|
+
next(error);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function requireRole(role, options) {
|
|
294
|
+
return (req, res, next) => __async(null, null, function* () {
|
|
295
|
+
try {
|
|
296
|
+
yield authenticateToken(req, __spreadProps(__spreadValues({}, options), {
|
|
297
|
+
requiredRole: role
|
|
298
|
+
}));
|
|
299
|
+
next();
|
|
300
|
+
} catch (error) {
|
|
301
|
+
logger.error("RequireRole middleware failed", {
|
|
302
|
+
requiredRole: role,
|
|
303
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
304
|
+
});
|
|
305
|
+
next(error);
|
|
201
306
|
}
|
|
202
307
|
});
|
|
203
308
|
}
|
|
@@ -294,6 +399,34 @@ var useAtlas = class {
|
|
|
294
399
|
useAtlas.mongoClient = null;
|
|
295
400
|
useAtlas.mongoDb = null;
|
|
296
401
|
|
|
402
|
+
// src/config.ts
|
|
403
|
+
var dotenv = __toESM(require("dotenv"));
|
|
404
|
+
dotenv.config();
|
|
405
|
+
function getEnv(key, fallback) {
|
|
406
|
+
const value = process.env[key];
|
|
407
|
+
if (value !== void 0) return value;
|
|
408
|
+
if (fallback !== void 0) return fallback;
|
|
409
|
+
throw new NotFoundError(`Missing required environment variable: ${key}`);
|
|
410
|
+
}
|
|
411
|
+
function getEnvNumber(key, fallback) {
|
|
412
|
+
const value = process.env[key];
|
|
413
|
+
if (value !== void 0) return Number(value);
|
|
414
|
+
if (fallback !== void 0) return fallback;
|
|
415
|
+
throw new NotFoundError(`Missing required environment variable: ${key}`);
|
|
416
|
+
}
|
|
417
|
+
function getEnvBoolean(key, fallback) {
|
|
418
|
+
const value = process.env[key];
|
|
419
|
+
if (value !== void 0) return value === "true";
|
|
420
|
+
if (fallback !== void 0) return fallback;
|
|
421
|
+
throw new NotFoundError(`Missing required environment variable: ${key}`);
|
|
422
|
+
}
|
|
423
|
+
var REDIS_HOST = getEnv("REDIS_HOST");
|
|
424
|
+
var REDIS_PORT = getEnvNumber("REDIS_PORT", 6379);
|
|
425
|
+
var REDIS_PASSWORD = getEnv("REDIS_PASSWORD");
|
|
426
|
+
var REDIS_TLS = getEnvBoolean("REDIS_TLS", true);
|
|
427
|
+
var CACHE_SHORT_TTL = getEnvNumber("CACHE_SHORT_TTL", 300);
|
|
428
|
+
var CACHE_LONG_TTL = getEnvNumber("CACHE_LONG_TTL", 3600);
|
|
429
|
+
|
|
297
430
|
// src/utils/ioredis.ts
|
|
298
431
|
var import_ioredis = __toESM(require("ioredis"));
|
|
299
432
|
var redisClient = null;
|
|
@@ -352,12 +485,11 @@ function useRedis() {
|
|
|
352
485
|
}
|
|
353
486
|
|
|
354
487
|
// src/utils/cache.ts
|
|
355
|
-
var DEFAULT_TTL = 300;
|
|
356
488
|
function useCache() {
|
|
357
489
|
function getRedisClient() {
|
|
358
490
|
return useRedis().getClient();
|
|
359
491
|
}
|
|
360
|
-
function
|
|
492
|
+
function getCache(cacheKey) {
|
|
361
493
|
return __async(this, null, function* () {
|
|
362
494
|
try {
|
|
363
495
|
const redisClient3 = getRedisClient();
|
|
@@ -371,13 +503,16 @@ function useCache() {
|
|
|
371
503
|
}
|
|
372
504
|
});
|
|
373
505
|
}
|
|
374
|
-
function
|
|
375
|
-
return __async(this, arguments, function* (cacheKey, data, ttl =
|
|
506
|
+
function setCache(_0, _1) {
|
|
507
|
+
return __async(this, arguments, function* (cacheKey, data, ttl = CACHE_SHORT_TTL, group) {
|
|
376
508
|
try {
|
|
377
509
|
const redisClient3 = getRedisClient();
|
|
378
510
|
yield redisClient3.set(cacheKey, JSON.stringify(data), "EX", ttl);
|
|
379
511
|
logger.info(`[Cache][Set] Stored key "${cacheKey}" with TTL ${ttl}s.`);
|
|
380
|
-
if (group)
|
|
512
|
+
if (group) {
|
|
513
|
+
yield redisClient3.sadd(`cache:group:${group}`, cacheKey);
|
|
514
|
+
yield redisClient3.expire(`cache:group:${group}`, CACHE_LONG_TTL);
|
|
515
|
+
}
|
|
381
516
|
} catch (error) {
|
|
382
517
|
logger.error(
|
|
383
518
|
`[Cache][Set] Failed to store key "${cacheKey}": ${error instanceof Error ? error.message : error}`
|
|
@@ -385,7 +520,7 @@ function useCache() {
|
|
|
385
520
|
}
|
|
386
521
|
});
|
|
387
522
|
}
|
|
388
|
-
function
|
|
523
|
+
function delCache(cacheKey) {
|
|
389
524
|
return __async(this, null, function* () {
|
|
390
525
|
try {
|
|
391
526
|
const redisClient3 = getRedisClient();
|
|
@@ -398,7 +533,7 @@ function useCache() {
|
|
|
398
533
|
}
|
|
399
534
|
});
|
|
400
535
|
}
|
|
401
|
-
function
|
|
536
|
+
function delCacheGroup(group) {
|
|
402
537
|
return __async(this, null, function* () {
|
|
403
538
|
try {
|
|
404
539
|
const redisClient3 = getRedisClient();
|
|
@@ -414,10 +549,10 @@ function useCache() {
|
|
|
414
549
|
});
|
|
415
550
|
}
|
|
416
551
|
return {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
552
|
+
getCache,
|
|
553
|
+
setCache,
|
|
554
|
+
delCache,
|
|
555
|
+
delCacheGroup
|
|
421
556
|
};
|
|
422
557
|
}
|
|
423
558
|
|
|
@@ -434,13 +569,58 @@ var import_path = __toESM(require("path"));
|
|
|
434
569
|
function getTemplatePath(directory, filePath) {
|
|
435
570
|
const ext = ".hbs";
|
|
436
571
|
const file = filePath.endsWith(ext) ? filePath : `${filePath}${ext}`;
|
|
437
|
-
|
|
572
|
+
const possiblePaths = [
|
|
573
|
+
import_path.default.join(process.cwd(), "src", "public", directory, file),
|
|
574
|
+
import_path.default.join(process.cwd(), "public", directory, file),
|
|
575
|
+
import_path.default.join(process.cwd(), directory, file)
|
|
576
|
+
];
|
|
577
|
+
const fs2 = require("fs");
|
|
578
|
+
for (const templatePath of possiblePaths) {
|
|
579
|
+
if (fs2.existsSync(templatePath)) {
|
|
580
|
+
return templatePath;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return possiblePaths[0];
|
|
438
584
|
}
|
|
439
585
|
|
|
440
586
|
// src/utils/handlebars-compiler.ts
|
|
441
587
|
var import_handlebars = __toESM(require("handlebars"));
|
|
442
588
|
var import_fs = require("fs");
|
|
443
589
|
var handlebarsTemplateCache = /* @__PURE__ */ new Map();
|
|
590
|
+
import_handlebars.default.registerHelper("formatCurrency", function(amount) {
|
|
591
|
+
if (typeof amount !== "number") return "0.00";
|
|
592
|
+
return new Intl.NumberFormat("en-PH", {
|
|
593
|
+
style: "decimal",
|
|
594
|
+
minimumFractionDigits: 2,
|
|
595
|
+
maximumFractionDigits: 2
|
|
596
|
+
}).format(amount);
|
|
597
|
+
});
|
|
598
|
+
import_handlebars.default.registerHelper(
|
|
599
|
+
"formatDate",
|
|
600
|
+
function(date, format2) {
|
|
601
|
+
if (!date) return "N/A";
|
|
602
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
603
|
+
if (isNaN(dateObj.getTime())) return "N/A";
|
|
604
|
+
const options = {};
|
|
605
|
+
if (format2.includes("MMMM")) options.month = "long";
|
|
606
|
+
else if (format2.includes("MMM")) options.month = "short";
|
|
607
|
+
else if (format2.includes("MM")) options.month = "2-digit";
|
|
608
|
+
if (format2.includes("D,")) options.day = "numeric";
|
|
609
|
+
else if (format2.includes("DD")) options.day = "2-digit";
|
|
610
|
+
if (format2.includes("YYYY")) options.year = "numeric";
|
|
611
|
+
else if (format2.includes("YY")) options.year = "2-digit";
|
|
612
|
+
if (format2.includes("h:mm A")) {
|
|
613
|
+
options.hour = "numeric";
|
|
614
|
+
options.minute = "2-digit";
|
|
615
|
+
options.hour12 = true;
|
|
616
|
+
} else if (format2.includes("HH:mm")) {
|
|
617
|
+
options.hour = "2-digit";
|
|
618
|
+
options.minute = "2-digit";
|
|
619
|
+
options.hour12 = false;
|
|
620
|
+
}
|
|
621
|
+
return new Intl.DateTimeFormat("en-US", options).format(dateObj);
|
|
622
|
+
}
|
|
623
|
+
);
|
|
444
624
|
function renderHandlebarsTemplate(_0) {
|
|
445
625
|
return __async(this, arguments, function* ({
|
|
446
626
|
context = {},
|
|
@@ -617,34 +797,6 @@ function hashPassword(_0) {
|
|
|
617
797
|
|
|
618
798
|
// src/utils/redis.ts
|
|
619
799
|
var import_redis = require("redis");
|
|
620
|
-
|
|
621
|
-
// src/config.ts
|
|
622
|
-
var dotenv = __toESM(require("dotenv"));
|
|
623
|
-
dotenv.config();
|
|
624
|
-
function getEnv(key, fallback) {
|
|
625
|
-
const value = process.env[key];
|
|
626
|
-
if (value !== void 0) return value;
|
|
627
|
-
if (fallback !== void 0) return fallback;
|
|
628
|
-
throw new Error(`Missing required environment variable: ${key}`);
|
|
629
|
-
}
|
|
630
|
-
function getEnvNumber(key, fallback) {
|
|
631
|
-
const value = process.env[key];
|
|
632
|
-
if (value !== void 0) return Number(value);
|
|
633
|
-
if (fallback !== void 0) return fallback;
|
|
634
|
-
throw new Error(`Missing required environment variable: ${key}`);
|
|
635
|
-
}
|
|
636
|
-
function getEnvBoolean(key, fallback) {
|
|
637
|
-
const value = process.env[key];
|
|
638
|
-
if (value !== void 0) return value === "true";
|
|
639
|
-
if (fallback !== void 0) return fallback;
|
|
640
|
-
throw new Error(`Missing required environment variable: ${key}`);
|
|
641
|
-
}
|
|
642
|
-
var REDIS_HOST = getEnv("REDIS_HOST");
|
|
643
|
-
var REDIS_PORT = getEnvNumber("REDIS_PORT", 6379);
|
|
644
|
-
var REDIS_PASSWORD = getEnv("REDIS_PASSWORD");
|
|
645
|
-
var REDIS_TLS = getEnvBoolean("REDIS_TLS", true);
|
|
646
|
-
|
|
647
|
-
// src/utils/redis.ts
|
|
648
800
|
var redisClient2 = null;
|
|
649
801
|
function initRedisClient() {
|
|
650
802
|
return __async(this, null, function* () {
|
|
@@ -767,6 +919,8 @@ function hashToken(token) {
|
|
|
767
919
|
logger,
|
|
768
920
|
paginate,
|
|
769
921
|
renderHandlebarsTemplate,
|
|
922
|
+
requireAdmin,
|
|
923
|
+
requireRole,
|
|
770
924
|
signJwtToken,
|
|
771
925
|
toObjectId,
|
|
772
926
|
useAtlas,
|