@exyconn/common 2.1.0 → 2.3.3
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/README.md +969 -261
- package/dist/client/hooks/index.d.mts +1042 -0
- package/dist/client/hooks/index.d.ts +1042 -0
- package/dist/client/hooks/index.js +2276 -0
- package/dist/client/hooks/index.js.map +1 -0
- package/dist/client/hooks/index.mjs +2217 -0
- package/dist/client/hooks/index.mjs.map +1 -0
- package/dist/client/http/index.d.mts +217 -49
- package/dist/client/http/index.d.ts +217 -49
- package/dist/client/http/index.js +473 -94
- package/dist/client/http/index.js.map +1 -1
- package/dist/client/http/index.mjs +441 -84
- package/dist/client/http/index.mjs.map +1 -1
- package/dist/client/index.d.mts +6 -4
- package/dist/client/index.d.ts +6 -4
- package/dist/client/index.js +481 -319
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +449 -290
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/utils/index.d.mts +3 -279
- package/dist/client/utils/index.d.ts +3 -279
- package/dist/client/web/index.d.mts +1461 -0
- package/dist/client/web/index.d.ts +1461 -0
- package/dist/client/web/index.js +2681 -0
- package/dist/client/web/index.js.map +1 -0
- package/dist/client/web/index.mjs +2618 -0
- package/dist/client/web/index.mjs.map +1 -0
- package/dist/data/brand-identity.d.mts +149 -0
- package/dist/data/brand-identity.d.ts +149 -0
- package/dist/data/brand-identity.js +235 -0
- package/dist/data/brand-identity.js.map +1 -0
- package/dist/data/brand-identity.mjs +220 -0
- package/dist/data/brand-identity.mjs.map +1 -0
- package/dist/data/countries.d.mts +61 -0
- package/dist/data/countries.d.ts +61 -0
- package/dist/data/countries.js +987 -0
- package/dist/data/countries.js.map +1 -0
- package/dist/data/countries.mjs +971 -0
- package/dist/data/countries.mjs.map +1 -0
- package/dist/data/currencies.d.mts +19 -0
- package/dist/data/currencies.d.ts +19 -0
- package/dist/data/currencies.js +162 -0
- package/dist/data/currencies.js.map +1 -0
- package/dist/data/currencies.mjs +153 -0
- package/dist/data/currencies.mjs.map +1 -0
- package/dist/data/index.d.mts +7 -0
- package/dist/data/index.d.ts +7 -0
- package/dist/data/index.js +2087 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/index.mjs +1948 -0
- package/dist/data/index.mjs.map +1 -0
- package/dist/data/phone-codes.d.mts +15 -0
- package/dist/data/phone-codes.d.ts +15 -0
- package/dist/data/phone-codes.js +219 -0
- package/dist/data/phone-codes.js.map +1 -0
- package/dist/data/phone-codes.mjs +211 -0
- package/dist/data/phone-codes.mjs.map +1 -0
- package/dist/data/regex.d.mts +287 -0
- package/dist/data/regex.d.ts +287 -0
- package/dist/data/regex.js +306 -0
- package/dist/data/regex.js.map +1 -0
- package/dist/data/regex.mjs +208 -0
- package/dist/data/regex.mjs.map +1 -0
- package/dist/data/timezones.d.mts +16 -0
- package/dist/data/timezones.d.ts +16 -0
- package/dist/data/timezones.js +98 -0
- package/dist/data/timezones.js.map +1 -0
- package/dist/data/timezones.mjs +89 -0
- package/dist/data/timezones.mjs.map +1 -0
- package/dist/index-BZf42T3R.d.mts +305 -0
- package/dist/index-CF0D8PGE.d.ts +305 -0
- package/dist/index-Ckhm_HaX.d.mts +138 -0
- package/dist/index-DKn4raO7.d.ts +222 -0
- package/dist/index-NS8dS0p9.d.mts +222 -0
- package/dist/index-Nqm5_lwT.d.ts +188 -0
- package/dist/index-br6POSyA.d.ts +138 -0
- package/dist/index-jBi3V6e5.d.mts +188 -0
- package/dist/index.d.mts +21 -580
- package/dist/index.d.ts +21 -580
- package/dist/index.js +1839 -347
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1850 -359
- package/dist/index.mjs.map +1 -1
- package/dist/packageCheck-B_qfsD6R.d.ts +280 -0
- package/dist/packageCheck-C2_FT_Rl.d.mts +280 -0
- package/dist/server/configs/index.d.mts +602 -0
- package/dist/server/configs/index.d.ts +602 -0
- package/dist/server/configs/index.js +707 -0
- package/dist/server/configs/index.js.map +1 -0
- package/dist/server/configs/index.mjs +665 -0
- package/dist/server/configs/index.mjs.map +1 -0
- package/dist/server/index.d.mts +4 -1
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.js +1330 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1286 -2
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/middleware/index.d.mts +283 -2
- package/dist/server/middleware/index.d.ts +283 -2
- package/dist/server/middleware/index.js +761 -0
- package/dist/server/middleware/index.js.map +1 -1
- package/dist/server/middleware/index.mjs +751 -1
- package/dist/server/middleware/index.mjs.map +1 -1
- package/dist/shared/config/index.d.mts +40 -0
- package/dist/shared/config/index.d.ts +40 -0
- package/dist/shared/config/index.js +58 -0
- package/dist/shared/config/index.js.map +1 -0
- package/dist/shared/config/index.mjs +51 -0
- package/dist/shared/config/index.mjs.map +1 -0
- package/dist/shared/constants/index.d.mts +593 -0
- package/dist/shared/constants/index.d.ts +593 -0
- package/dist/shared/constants/index.js +391 -0
- package/dist/shared/constants/index.js.map +1 -0
- package/dist/shared/constants/index.mjs +360 -0
- package/dist/shared/constants/index.mjs.map +1 -0
- package/dist/shared/index.d.mts +5 -1
- package/dist/shared/index.d.ts +5 -1
- package/dist/shared/types/index.d.mts +140 -0
- package/dist/shared/types/index.d.ts +140 -0
- package/dist/shared/types/index.js +4 -0
- package/dist/shared/types/index.js.map +1 -0
- package/dist/shared/types/index.mjs +3 -0
- package/dist/shared/types/index.mjs.map +1 -0
- package/dist/shared/utils/index.d.mts +255 -0
- package/dist/shared/utils/index.d.ts +255 -0
- package/dist/shared/utils/index.js +623 -0
- package/dist/shared/utils/index.js.map +1 -0
- package/dist/shared/utils/index.mjs +324 -0
- package/dist/shared/utils/index.mjs.map +1 -0
- package/dist/shared/validation/index.d.mts +258 -0
- package/dist/shared/validation/index.d.ts +258 -0
- package/dist/shared/validation/index.js +185 -0
- package/dist/shared/validation/index.js.map +1 -0
- package/dist/shared/validation/index.mjs +172 -0
- package/dist/shared/validation/index.mjs.map +1 -0
- package/package.json +127 -62
- package/dist/index-BcxL4_V4.d.ts +0 -2946
- package/dist/index-DEzgM15j.d.ts +0 -67
- package/dist/index-DNFVgQx8.d.ts +0 -1375
- package/dist/index-DbV04Dx8.d.mts +0 -67
- package/dist/index-DfqEP6Oe.d.mts +0 -1375
- package/dist/index-bvvCev9Q.d.mts +0 -2946
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import winston from 'winston';
|
|
2
2
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
3
3
|
import path, { resolve } from 'path';
|
|
4
|
-
import mongoose from 'mongoose';
|
|
4
|
+
import mongoose, { Types } from 'mongoose';
|
|
5
5
|
import jwt from 'jsonwebtoken';
|
|
6
6
|
import { existsSync, readFileSync } from 'fs';
|
|
7
|
+
import rateLimit from 'express-rate-limit';
|
|
7
8
|
import axios from 'axios';
|
|
8
9
|
import { createContext, useState, useCallback, useMemo, useRef, useEffect, useContext } from 'react';
|
|
9
10
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
11
|
import * as Yup from 'yup';
|
|
11
12
|
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
|
12
|
-
import { subYears, subWeeks, subMonths, subMinutes, subHours, subDays, startOfYear, startOfWeek, startOfMonth, startOfDay
|
|
13
|
+
import { subYears, subWeeks, subMonths, subMinutes, subHours, subDays, startOfYear, startOfWeek, startOfMonth, startOfDay, setSeconds, setMinutes, setHours, parseISO, isYesterday, isValid, isTomorrow, isToday, isThisYear, isThisWeek, isThisMonth, isSameYear, isSameWeek, isSameMonth, isSameDay, isPast, isFuture, isEqual, isBefore, isAfter, intervalToDuration, getYear, getSeconds, getMonth, getMinutes, getHours, getDay, getDate, formatRelative, formatDuration, formatDistanceToNow, formatDistance, format, endOfYear, endOfWeek, endOfMonth, endOfDay, eachWeekOfInterval, eachMonthOfInterval, eachDayOfInterval, differenceInYears, differenceInWeeks, differenceInSeconds, differenceInMonths, differenceInMinutes, differenceInHours, differenceInDays, addYears, addWeeks, addMonths, addMinutes, addHours, addDays } from 'date-fns';
|
|
13
14
|
import { toZonedTime, fromZonedTime, formatInTimeZone } from 'date-fns-tz';
|
|
14
15
|
|
|
15
16
|
var __defProp = Object.defineProperty;
|
|
@@ -21,44 +22,89 @@ var __export = (target, all) => {
|
|
|
21
22
|
// src/server/index.ts
|
|
22
23
|
var server_exports = {};
|
|
23
24
|
__export(server_exports, {
|
|
25
|
+
ConfigBuilder: () => ConfigBuilder,
|
|
26
|
+
DEFAULT_AUTH_CONFIG: () => DEFAULT_AUTH_CONFIG,
|
|
27
|
+
DEFAULT_CORS_CONFIG: () => DEFAULT_CORS_CONFIG,
|
|
28
|
+
DEFAULT_CORS_ORIGINS: () => DEFAULT_CORS_ORIGINS,
|
|
29
|
+
DEFAULT_DATABASE_CONFIG: () => DEFAULT_DATABASE_CONFIG,
|
|
30
|
+
DEFAULT_LOGGING_CONFIG: () => DEFAULT_LOGGING_CONFIG,
|
|
31
|
+
DEFAULT_RATE_LIMIT_CONFIG: () => DEFAULT_RATE_LIMIT_CONFIG,
|
|
32
|
+
DEFAULT_RATE_LIMIT_TIERS: () => DEFAULT_RATE_LIMIT_TIERS,
|
|
33
|
+
DEFAULT_SERVER_CONFIG: () => DEFAULT_SERVER_CONFIG,
|
|
34
|
+
EXYCONN_CORS_CONFIG: () => EXYCONN_CORS_CONFIG,
|
|
35
|
+
PERMISSIVE_CORS_CONFIG: () => PERMISSIVE_CORS_CONFIG,
|
|
36
|
+
RATE_LIMIT_CONFIG: () => RATE_LIMIT_CONFIG,
|
|
37
|
+
RateLimiterBuilder: () => RateLimiterBuilder,
|
|
38
|
+
STRICT_CORS_CONFIG: () => STRICT_CORS_CONFIG,
|
|
24
39
|
StatusCode: () => StatusCode,
|
|
25
40
|
StatusMessage: () => StatusMessage,
|
|
26
41
|
authenticateApiKey: () => authenticateApiKey,
|
|
27
42
|
authenticateJWT: () => authenticateJWT,
|
|
28
43
|
badRequestResponse: () => badRequestResponse,
|
|
44
|
+
buildConfig: () => buildConfig,
|
|
45
|
+
buildDeleteFilter: () => buildDeleteFilter,
|
|
29
46
|
buildFilter: () => buildFilter,
|
|
30
47
|
buildPagination: () => buildPagination,
|
|
31
48
|
buildPaginationMeta: () => buildPaginationMeta,
|
|
32
49
|
checkPackageServer: () => checkPackageServer,
|
|
33
50
|
conflictResponse: () => conflictResponse,
|
|
34
51
|
connectDB: () => connectDB,
|
|
52
|
+
corsOptions: () => corsOptions,
|
|
53
|
+
createApiKeyGenerator: () => createApiKeyGenerator,
|
|
54
|
+
createApiRateLimiter: () => createApiRateLimiter,
|
|
55
|
+
createBrandCorsOptions: () => createBrandCorsOptions,
|
|
56
|
+
createBulkDeleteHandler: () => createBulkDeleteHandler,
|
|
57
|
+
createConfig: () => createConfig,
|
|
58
|
+
createCorsOptions: () => createCorsOptions,
|
|
59
|
+
createCrudControllers: () => createCrudControllers,
|
|
60
|
+
createDdosRateLimiter: () => createDdosRateLimiter,
|
|
35
61
|
createLogger: () => createLogger,
|
|
36
62
|
createMorganStream: () => createMorganStream,
|
|
63
|
+
createMultiBrandCorsOptions: () => createMultiBrandCorsOptions,
|
|
64
|
+
createPaginationMiddleware: () => createPaginationMiddleware,
|
|
65
|
+
createPrefixedKeyGenerator: () => createPrefixedKeyGenerator,
|
|
66
|
+
createRateLimiter: () => createRateLimiter,
|
|
67
|
+
createStandardRateLimiter: () => createStandardRateLimiter,
|
|
68
|
+
createStrictRateLimiter: () => createStrictRateLimiter,
|
|
69
|
+
createUserKeyGenerator: () => createUserKeyGenerator,
|
|
37
70
|
createdResponse: () => createdResponse,
|
|
71
|
+
ddosProtectionLimiter: () => ddosProtectionLimiter,
|
|
72
|
+
defaultKeyGenerator: () => defaultKeyGenerator,
|
|
38
73
|
disconnectDB: () => disconnectDB,
|
|
39
74
|
errorResponse: () => errorResponse,
|
|
40
75
|
extractColumns: () => extractColumns,
|
|
41
76
|
extractOrganization: () => extractOrganization,
|
|
77
|
+
extractSchemaMeta: () => extractSchemaMeta,
|
|
42
78
|
forbiddenResponse: () => forbiddenResponse,
|
|
43
79
|
formatPackageCheckResult: () => formatPackageCheckResult,
|
|
44
80
|
generateNcuCommand: () => generateNcuCommand,
|
|
45
81
|
getConnectionStatus: () => getConnectionStatus,
|
|
82
|
+
getDatabaseOptions: () => getDatabaseOptions,
|
|
83
|
+
isDevelopment: () => isDevelopment,
|
|
84
|
+
isProduction: () => isProduction,
|
|
85
|
+
isTest: () => isTest,
|
|
46
86
|
logger: () => logger,
|
|
47
87
|
noContentResponse: () => noContentResponse,
|
|
48
88
|
notFoundResponse: () => notFoundResponse,
|
|
49
89
|
omitFields: () => omitFields,
|
|
50
90
|
optionalAuthenticateJWT: () => optionalAuthenticateJWT,
|
|
51
91
|
packageCheckServer: () => packageCheckServer,
|
|
92
|
+
parseBulkDelete: () => parseBulkDelete,
|
|
52
93
|
pickFields: () => pickFields,
|
|
53
94
|
printPackageCheckSummary: () => printPackageCheckSummary,
|
|
95
|
+
queryPagination: () => queryPagination,
|
|
96
|
+
queryParser: () => queryParser,
|
|
54
97
|
rateLimitResponse: () => rateLimitResponse,
|
|
98
|
+
rateLimiter: () => rateLimiter,
|
|
55
99
|
requireOrganization: () => requireOrganization,
|
|
56
100
|
sanitizeDocument: () => sanitizeDocument,
|
|
57
101
|
sanitizeUser: () => sanitizeUser,
|
|
58
102
|
simpleLogger: () => simpleLogger,
|
|
103
|
+
standardRateLimiter: () => standardRateLimiter,
|
|
59
104
|
statusCode: () => statusCode,
|
|
60
105
|
statusMessage: () => statusMessage,
|
|
61
106
|
stream: () => stream,
|
|
107
|
+
strictRateLimiter: () => strictRateLimiter,
|
|
62
108
|
successResponse: () => successResponse,
|
|
63
109
|
successResponseArr: () => successResponseArr,
|
|
64
110
|
unauthorizedResponse: () => unauthorizedResponse,
|
|
@@ -341,37 +387,37 @@ var defaultOptions = {
|
|
|
341
387
|
retryReads: true,
|
|
342
388
|
w: "majority"
|
|
343
389
|
};
|
|
344
|
-
var connectDB = async (mongoUri, options = {},
|
|
390
|
+
var connectDB = async (mongoUri, options = {}, logger3) => {
|
|
345
391
|
if (mongoose.connection.readyState === 1) {
|
|
346
|
-
|
|
392
|
+
logger3?.info("Database already connected, reusing connection");
|
|
347
393
|
return mongoose;
|
|
348
394
|
}
|
|
349
395
|
const finalOptions = { ...defaultOptions, ...options };
|
|
350
396
|
try {
|
|
351
397
|
await mongoose.connect(mongoUri, finalOptions);
|
|
352
|
-
|
|
398
|
+
logger3?.info("MongoDB connected successfully");
|
|
353
399
|
mongoose.connection.on("error", (err) => {
|
|
354
|
-
|
|
400
|
+
logger3?.error("MongoDB connection error", err);
|
|
355
401
|
});
|
|
356
402
|
mongoose.connection.on("disconnected", () => {
|
|
357
|
-
|
|
403
|
+
logger3?.info("MongoDB disconnected");
|
|
358
404
|
});
|
|
359
405
|
mongoose.connection.on("reconnected", () => {
|
|
360
|
-
|
|
406
|
+
logger3?.info("MongoDB reconnected");
|
|
361
407
|
});
|
|
362
408
|
return mongoose;
|
|
363
409
|
} catch (error) {
|
|
364
410
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
365
|
-
|
|
411
|
+
logger3?.error("MongoDB connection failed", { error: errorMessage });
|
|
366
412
|
throw error;
|
|
367
413
|
}
|
|
368
414
|
};
|
|
369
|
-
var disconnectDB = async (
|
|
415
|
+
var disconnectDB = async (logger3) => {
|
|
370
416
|
try {
|
|
371
417
|
await mongoose.disconnect();
|
|
372
|
-
|
|
418
|
+
logger3?.info("MongoDB disconnected successfully");
|
|
373
419
|
} catch (error) {
|
|
374
|
-
|
|
420
|
+
logger3?.error("Error disconnecting from MongoDB", error);
|
|
375
421
|
throw error;
|
|
376
422
|
}
|
|
377
423
|
};
|
|
@@ -476,6 +522,629 @@ var requireOrganization = (req, res, next) => {
|
|
|
476
522
|
next();
|
|
477
523
|
};
|
|
478
524
|
|
|
525
|
+
// src/server/middleware/queryParser.middleware.ts
|
|
526
|
+
var queryParser = (req, _, next) => {
|
|
527
|
+
const { page, limit, sort, sortBy, sortOrder, search, filter, ...otherParams } = req.query;
|
|
528
|
+
const parsed = {
|
|
529
|
+
page: Math.max(Number(page) || 1, 1),
|
|
530
|
+
limit: Math.min(Number(limit) || 10, 100),
|
|
531
|
+
filter: {}
|
|
532
|
+
};
|
|
533
|
+
if (typeof sort === "string") {
|
|
534
|
+
const [field, order] = sort.split(":");
|
|
535
|
+
parsed.sort = {
|
|
536
|
+
field,
|
|
537
|
+
order: order === "asc" ? "asc" : "desc"
|
|
538
|
+
};
|
|
539
|
+
} else if (typeof sortBy === "string") {
|
|
540
|
+
parsed.sort = {
|
|
541
|
+
field: sortBy,
|
|
542
|
+
order: sortOrder === "asc" ? "asc" : "desc"
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
if (typeof search === "string") {
|
|
546
|
+
parsed.search = search;
|
|
547
|
+
}
|
|
548
|
+
if (typeof filter === "object" && filter !== null) {
|
|
549
|
+
Object.entries(filter).forEach(([key, value]) => {
|
|
550
|
+
if (value !== "all") {
|
|
551
|
+
parsed.filter[key] = value;
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
Object.entries(otherParams).forEach(([key, value]) => {
|
|
556
|
+
if (typeof value === "string" && value !== "all" && !["page", "limit", "sort", "sortBy", "sortOrder", "search"].includes(key)) {
|
|
557
|
+
parsed.filter[key] = value;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
req.parsedQuery = parsed;
|
|
561
|
+
next();
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/server/middleware/utils/schemaMeta.util.ts
|
|
565
|
+
var getZodTypeName = (schema) => {
|
|
566
|
+
const typeName = schema._def?.typeName;
|
|
567
|
+
switch (typeName) {
|
|
568
|
+
case "ZodString":
|
|
569
|
+
return "string";
|
|
570
|
+
case "ZodNumber":
|
|
571
|
+
return "number";
|
|
572
|
+
case "ZodBoolean":
|
|
573
|
+
return "boolean";
|
|
574
|
+
case "ZodDate":
|
|
575
|
+
return "date";
|
|
576
|
+
case "ZodArray":
|
|
577
|
+
return "array";
|
|
578
|
+
case "ZodObject":
|
|
579
|
+
return "object";
|
|
580
|
+
case "ZodOptional":
|
|
581
|
+
case "ZodNullable":
|
|
582
|
+
return schema._def?.innerType ? getZodTypeName(schema._def.innerType) : "unknown";
|
|
583
|
+
case "ZodDefault":
|
|
584
|
+
return schema._def?.innerType ? getZodTypeName(schema._def.innerType) : "unknown";
|
|
585
|
+
case "ZodEnum":
|
|
586
|
+
return "enum";
|
|
587
|
+
case "ZodUnion":
|
|
588
|
+
return "union";
|
|
589
|
+
default:
|
|
590
|
+
return "unknown";
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
var isZodRequired = (schema) => {
|
|
594
|
+
const typeName = schema._def?.typeName;
|
|
595
|
+
return typeName !== "ZodOptional" && typeName !== "ZodNullable";
|
|
596
|
+
};
|
|
597
|
+
var extractSchemaMeta = (model, zodSchema) => {
|
|
598
|
+
const columns = [];
|
|
599
|
+
if (zodSchema && zodSchema.shape) {
|
|
600
|
+
const shape = zodSchema.shape;
|
|
601
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
602
|
+
if (key.startsWith("_")) continue;
|
|
603
|
+
columns.push({
|
|
604
|
+
name: key,
|
|
605
|
+
datatype: getZodTypeName(value),
|
|
606
|
+
required: isZodRequired(value)
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return columns;
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
const schema = model.schema;
|
|
613
|
+
const paths = schema.paths;
|
|
614
|
+
for (const [key, pathInfo] of Object.entries(paths)) {
|
|
615
|
+
if (key.startsWith("_") || key === "__v") continue;
|
|
616
|
+
const schemaType = pathInfo;
|
|
617
|
+
columns.push({
|
|
618
|
+
name: key,
|
|
619
|
+
datatype: (schemaType.instance || "unknown").toLowerCase(),
|
|
620
|
+
required: schemaType.isRequired || false
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
} catch {
|
|
624
|
+
}
|
|
625
|
+
return columns;
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// src/server/middleware/pagination.middleware.ts
|
|
629
|
+
var queryPagination = (model, options = {}, withOrgId = true) => {
|
|
630
|
+
return async (req, res, next) => {
|
|
631
|
+
try {
|
|
632
|
+
const { page, limit, sort, search, filter } = req.parsedQuery;
|
|
633
|
+
const query = {};
|
|
634
|
+
Object.entries(filter).forEach(([key, value]) => {
|
|
635
|
+
if (options.regexFilterFields?.includes(key)) {
|
|
636
|
+
query[key] = { $regex: value, $options: "i" };
|
|
637
|
+
} else {
|
|
638
|
+
query[key] = value;
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
const organizationId = req.headers["x-organization-id"];
|
|
642
|
+
if (organizationId && typeof organizationId === "string" && withOrgId) {
|
|
643
|
+
query.organizationId = organizationId;
|
|
644
|
+
}
|
|
645
|
+
if (search && options.searchFields?.length) {
|
|
646
|
+
query.$or = options.searchFields.map((field) => ({
|
|
647
|
+
[field]: { $regex: search, $options: "i" }
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
const sortQuery = sort ? { [sort.field]: sort.order } : { createdAt: "desc" };
|
|
651
|
+
const skip = (page - 1) * limit;
|
|
652
|
+
const [data, total] = await Promise.all([
|
|
653
|
+
model.find(query).sort(sortQuery).skip(skip).limit(limit),
|
|
654
|
+
model.countDocuments(query)
|
|
655
|
+
]);
|
|
656
|
+
res.paginatedResult = {
|
|
657
|
+
data,
|
|
658
|
+
meta: {
|
|
659
|
+
page,
|
|
660
|
+
limit,
|
|
661
|
+
total,
|
|
662
|
+
totalPages: Math.ceil(total / limit)
|
|
663
|
+
},
|
|
664
|
+
columns: extractSchemaMeta(model, options.validatorSchema)
|
|
665
|
+
};
|
|
666
|
+
next();
|
|
667
|
+
} catch (error) {
|
|
668
|
+
next(error);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
var isZodError = (error) => {
|
|
673
|
+
return error !== null && typeof error === "object" && "errors" in error && Array.isArray(error.errors);
|
|
674
|
+
};
|
|
675
|
+
var getOrgId = (req, orgField = "organizationId") => {
|
|
676
|
+
const orgReq = req;
|
|
677
|
+
return orgReq.organizationId || req.headers["x-organization-id"] || req.query[orgField];
|
|
678
|
+
};
|
|
679
|
+
var buildOrgFilter = (req, config) => {
|
|
680
|
+
const filter = {};
|
|
681
|
+
if (config.withOrganization !== false) {
|
|
682
|
+
const orgId = getOrgId(req, config.orgField);
|
|
683
|
+
if (orgId) {
|
|
684
|
+
filter[config.orgField || "organizationId"] = orgId;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return filter;
|
|
688
|
+
};
|
|
689
|
+
var formatZodError = (error) => {
|
|
690
|
+
return error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
691
|
+
};
|
|
692
|
+
function createCrudControllers(config) {
|
|
693
|
+
const {
|
|
694
|
+
model,
|
|
695
|
+
resourceName,
|
|
696
|
+
createSchema,
|
|
697
|
+
updateSchema,
|
|
698
|
+
searchFields = [],
|
|
699
|
+
regexFilterFields = [],
|
|
700
|
+
withOrganization = true,
|
|
701
|
+
orgField = "organizationId",
|
|
702
|
+
transformCreate,
|
|
703
|
+
transformUpdate,
|
|
704
|
+
afterCreate,
|
|
705
|
+
afterUpdate,
|
|
706
|
+
afterDelete,
|
|
707
|
+
excludeFields = [],
|
|
708
|
+
populateFields = [],
|
|
709
|
+
buildQuery
|
|
710
|
+
} = config;
|
|
711
|
+
const getAll = async (req, res, _next) => {
|
|
712
|
+
try {
|
|
713
|
+
const paginatedRes = res;
|
|
714
|
+
if (paginatedRes.paginatedResult) {
|
|
715
|
+
successResponse(res, paginatedRes.paginatedResult, `${resourceName} list fetched successfully`);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const page = parseInt(req.query.page) || 1;
|
|
719
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
720
|
+
const sortField = req.query.sortBy || "createdAt";
|
|
721
|
+
const sortOrder = req.query.sortOrder || "desc";
|
|
722
|
+
const search = req.query.search;
|
|
723
|
+
let query = {};
|
|
724
|
+
if (withOrganization) {
|
|
725
|
+
const orgId = getOrgId(req, orgField);
|
|
726
|
+
if (orgId) {
|
|
727
|
+
query[orgField] = orgId;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (search && searchFields.length > 0) {
|
|
731
|
+
query.$or = searchFields.map((field) => ({
|
|
732
|
+
[field]: { $regex: search, $options: "i" }
|
|
733
|
+
}));
|
|
734
|
+
}
|
|
735
|
+
const filterableParams = Object.keys(req.query).filter(
|
|
736
|
+
(key) => !["page", "limit", "sortBy", "sortOrder", "search"].includes(key)
|
|
737
|
+
);
|
|
738
|
+
filterableParams.forEach((key) => {
|
|
739
|
+
const value = req.query[key];
|
|
740
|
+
if (value !== void 0 && value !== "" && value !== "all") {
|
|
741
|
+
if (regexFilterFields.includes(key)) {
|
|
742
|
+
query[key] = { $regex: value, $options: "i" };
|
|
743
|
+
} else {
|
|
744
|
+
query[key] = value;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
if (buildQuery) {
|
|
749
|
+
query = buildQuery(req, query);
|
|
750
|
+
}
|
|
751
|
+
const sortQuery = { [sortField]: sortOrder };
|
|
752
|
+
const skip = (page - 1) * limit;
|
|
753
|
+
let projection = {};
|
|
754
|
+
if (excludeFields.length > 0) {
|
|
755
|
+
projection = excludeFields.reduce(
|
|
756
|
+
(acc, field) => ({ ...acc, [field]: 0 }),
|
|
757
|
+
{}
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
let dbQuery = model.find(query, projection);
|
|
761
|
+
if (populateFields.length > 0) {
|
|
762
|
+
populateFields.forEach((field) => {
|
|
763
|
+
dbQuery = dbQuery.populate(field);
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
const [data, total] = await Promise.all([
|
|
767
|
+
dbQuery.sort(sortQuery).skip(skip).limit(limit),
|
|
768
|
+
model.countDocuments(query)
|
|
769
|
+
]);
|
|
770
|
+
successResponse(
|
|
771
|
+
res,
|
|
772
|
+
{
|
|
773
|
+
data,
|
|
774
|
+
meta: {
|
|
775
|
+
page,
|
|
776
|
+
limit,
|
|
777
|
+
total,
|
|
778
|
+
totalPages: Math.ceil(total / limit)
|
|
779
|
+
},
|
|
780
|
+
columns: extractSchemaMeta(model, createSchema)
|
|
781
|
+
},
|
|
782
|
+
`${resourceName} list fetched successfully`
|
|
783
|
+
);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
logger.error(`Error in getAll ${resourceName}`, {
|
|
786
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
787
|
+
});
|
|
788
|
+
errorResponse(res, `Failed to fetch ${resourceName.toLowerCase()} list`);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
const getById = async (req, res, _next) => {
|
|
792
|
+
try {
|
|
793
|
+
const { id } = req.params;
|
|
794
|
+
if (!id || !Types.ObjectId.isValid(id)) {
|
|
795
|
+
badRequestResponse(res, "Invalid ID format");
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
const query = {
|
|
799
|
+
_id: new Types.ObjectId(id),
|
|
800
|
+
...buildOrgFilter(req, { ...config, withOrganization, orgField })
|
|
801
|
+
};
|
|
802
|
+
let dbQuery = model.findOne(query);
|
|
803
|
+
if (populateFields.length > 0) {
|
|
804
|
+
populateFields.forEach((field) => {
|
|
805
|
+
dbQuery = dbQuery.populate(field);
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
const doc = await dbQuery;
|
|
809
|
+
if (!doc) {
|
|
810
|
+
notFoundResponse(res, `${resourceName} not found`);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
successResponse(res, doc, `${resourceName} fetched successfully`);
|
|
814
|
+
} catch (error) {
|
|
815
|
+
logger.error(`Error in getById ${resourceName}`, {
|
|
816
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
817
|
+
id: req.params.id
|
|
818
|
+
});
|
|
819
|
+
errorResponse(res, `Failed to fetch ${resourceName.toLowerCase()}`);
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
const create = async (req, res, _next) => {
|
|
823
|
+
try {
|
|
824
|
+
let input = req.body;
|
|
825
|
+
if (createSchema) {
|
|
826
|
+
try {
|
|
827
|
+
input = createSchema.parse(input);
|
|
828
|
+
} catch (error) {
|
|
829
|
+
if (isZodError(error)) {
|
|
830
|
+
badRequestResponse(res, formatZodError(error));
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
throw error;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (transformCreate) {
|
|
837
|
+
input = transformCreate(input, req);
|
|
838
|
+
}
|
|
839
|
+
if (withOrganization) {
|
|
840
|
+
const orgId = getOrgId(req, orgField);
|
|
841
|
+
if (orgId) {
|
|
842
|
+
input[orgField] = orgId;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const doc = new model(input);
|
|
846
|
+
await doc.save();
|
|
847
|
+
if (afterCreate) {
|
|
848
|
+
await afterCreate(doc, req);
|
|
849
|
+
}
|
|
850
|
+
logger.info(`${resourceName} created successfully`, {
|
|
851
|
+
id: doc._id,
|
|
852
|
+
[orgField]: input[orgField]
|
|
853
|
+
});
|
|
854
|
+
createdResponse(res, doc, `${resourceName} created successfully`);
|
|
855
|
+
} catch (error) {
|
|
856
|
+
logger.error(`Error in create ${resourceName}`, {
|
|
857
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
858
|
+
});
|
|
859
|
+
if (error.code === 11e3) {
|
|
860
|
+
badRequestResponse(res, `A ${resourceName.toLowerCase()} with this data already exists`);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
errorResponse(res, `Failed to create ${resourceName.toLowerCase()}`);
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const update = async (req, res, _next) => {
|
|
867
|
+
try {
|
|
868
|
+
const { id } = req.params;
|
|
869
|
+
if (!id || !Types.ObjectId.isValid(id)) {
|
|
870
|
+
badRequestResponse(res, "Invalid ID format");
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
let input = req.body;
|
|
874
|
+
if (updateSchema) {
|
|
875
|
+
try {
|
|
876
|
+
input = updateSchema.parse(input);
|
|
877
|
+
} catch (error) {
|
|
878
|
+
if (isZodError(error)) {
|
|
879
|
+
badRequestResponse(res, formatZodError(error));
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
throw error;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (transformUpdate) {
|
|
886
|
+
input = transformUpdate(input, req);
|
|
887
|
+
}
|
|
888
|
+
const query = {
|
|
889
|
+
_id: new Types.ObjectId(id),
|
|
890
|
+
...buildOrgFilter(req, { ...config, withOrganization, orgField })
|
|
891
|
+
};
|
|
892
|
+
const doc = await model.findOneAndUpdate(query, { $set: input }, { new: true });
|
|
893
|
+
if (!doc) {
|
|
894
|
+
notFoundResponse(res, `${resourceName} not found`);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
if (afterUpdate) {
|
|
898
|
+
await afterUpdate(doc, req);
|
|
899
|
+
}
|
|
900
|
+
logger.info(`${resourceName} updated successfully`, { id });
|
|
901
|
+
successResponse(res, doc, `${resourceName} updated successfully`);
|
|
902
|
+
} catch (error) {
|
|
903
|
+
logger.error(`Error in update ${resourceName}`, {
|
|
904
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
905
|
+
id: req.params.id
|
|
906
|
+
});
|
|
907
|
+
errorResponse(res, `Failed to update ${resourceName.toLowerCase()}`);
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const deleteOne = async (req, res, _next) => {
|
|
911
|
+
try {
|
|
912
|
+
const { id } = req.params;
|
|
913
|
+
if (!id || !Types.ObjectId.isValid(id)) {
|
|
914
|
+
badRequestResponse(res, "Invalid ID format");
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
const query = {
|
|
918
|
+
_id: new Types.ObjectId(id),
|
|
919
|
+
...buildOrgFilter(req, { ...config, withOrganization, orgField })
|
|
920
|
+
};
|
|
921
|
+
const result = await model.deleteOne(query);
|
|
922
|
+
if (result.deletedCount === 0) {
|
|
923
|
+
notFoundResponse(res, `${resourceName} not found`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (afterDelete) {
|
|
927
|
+
await afterDelete(id, req);
|
|
928
|
+
}
|
|
929
|
+
logger.info(`${resourceName} deleted successfully`, { id });
|
|
930
|
+
noContentResponse(res, null, `${resourceName} deleted successfully`);
|
|
931
|
+
} catch (error) {
|
|
932
|
+
logger.error(`Error in delete ${resourceName}`, {
|
|
933
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
934
|
+
id: req.params.id
|
|
935
|
+
});
|
|
936
|
+
errorResponse(res, `Failed to delete ${resourceName.toLowerCase()}`);
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
const bulkDelete = async (req, res, _next) => {
|
|
940
|
+
try {
|
|
941
|
+
const bulkReq = req;
|
|
942
|
+
const { deleteIds = [], deleteAll = false } = bulkReq;
|
|
943
|
+
const baseFilter = buildOrgFilter(req, { ...config, withOrganization, orgField });
|
|
944
|
+
let filter;
|
|
945
|
+
if (deleteAll) {
|
|
946
|
+
filter = baseFilter;
|
|
947
|
+
} else if (deleteIds.length > 0) {
|
|
948
|
+
filter = {
|
|
949
|
+
...baseFilter,
|
|
950
|
+
_id: { $in: deleteIds.map((id) => new Types.ObjectId(id)) }
|
|
951
|
+
};
|
|
952
|
+
} else {
|
|
953
|
+
badRequestResponse(res, "No IDs provided for deletion");
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const result = await model.deleteMany(filter);
|
|
957
|
+
if (afterDelete && deleteIds.length > 0) {
|
|
958
|
+
await Promise.all(deleteIds.map((id) => afterDelete(id, req)));
|
|
959
|
+
}
|
|
960
|
+
logger.info(`${resourceName}(s) bulk deleted successfully`, {
|
|
961
|
+
deletedCount: result.deletedCount,
|
|
962
|
+
deleteAll
|
|
963
|
+
});
|
|
964
|
+
successResponse(
|
|
965
|
+
res,
|
|
966
|
+
{ deletedCount: result.deletedCount },
|
|
967
|
+
`${result.deletedCount} ${resourceName.toLowerCase()}(s) deleted successfully`
|
|
968
|
+
);
|
|
969
|
+
} catch (error) {
|
|
970
|
+
logger.error(`Error in bulkDelete ${resourceName}`, {
|
|
971
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
972
|
+
});
|
|
973
|
+
errorResponse(res, `Failed to delete ${resourceName.toLowerCase()}(s)`);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
return {
|
|
977
|
+
getAll,
|
|
978
|
+
getById,
|
|
979
|
+
create,
|
|
980
|
+
update,
|
|
981
|
+
deleteOne,
|
|
982
|
+
bulkDelete
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function createPaginationMiddleware(model, config = {}) {
|
|
986
|
+
const {
|
|
987
|
+
searchFields = [],
|
|
988
|
+
regexFilterFields = [],
|
|
989
|
+
withOrganization = true,
|
|
990
|
+
orgField = "organizationId"
|
|
991
|
+
} = config;
|
|
992
|
+
return async (req, res, next) => {
|
|
993
|
+
try {
|
|
994
|
+
const page = parseInt(req.query.page) || 1;
|
|
995
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
996
|
+
const sortField = req.query.sortBy || "createdAt";
|
|
997
|
+
const sortOrder = req.query.sortOrder || "desc";
|
|
998
|
+
const search = req.query.search;
|
|
999
|
+
const query = {};
|
|
1000
|
+
if (withOrganization) {
|
|
1001
|
+
const orgId = getOrgId(req, orgField);
|
|
1002
|
+
if (orgId) {
|
|
1003
|
+
query[orgField] = orgId;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (search && searchFields.length > 0) {
|
|
1007
|
+
query.$or = searchFields.map((field) => ({
|
|
1008
|
+
[field]: { $regex: search, $options: "i" }
|
|
1009
|
+
}));
|
|
1010
|
+
}
|
|
1011
|
+
const filterableParams = Object.keys(req.query).filter(
|
|
1012
|
+
(key) => !["page", "limit", "sortBy", "sortOrder", "search"].includes(key)
|
|
1013
|
+
);
|
|
1014
|
+
filterableParams.forEach((key) => {
|
|
1015
|
+
const value = req.query[key];
|
|
1016
|
+
if (value !== void 0 && value !== "" && value !== "all") {
|
|
1017
|
+
if (regexFilterFields.includes(key)) {
|
|
1018
|
+
query[key] = { $regex: value, $options: "i" };
|
|
1019
|
+
} else {
|
|
1020
|
+
query[key] = value;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
const sortQuery = { [sortField]: sortOrder };
|
|
1025
|
+
const skip = (page - 1) * limit;
|
|
1026
|
+
const [data, total] = await Promise.all([
|
|
1027
|
+
model.find(query).sort(sortQuery).skip(skip).limit(limit),
|
|
1028
|
+
model.countDocuments(query)
|
|
1029
|
+
]);
|
|
1030
|
+
const paginatedRes = res;
|
|
1031
|
+
paginatedRes.paginatedResult = {
|
|
1032
|
+
data,
|
|
1033
|
+
meta: {
|
|
1034
|
+
page,
|
|
1035
|
+
limit,
|
|
1036
|
+
total,
|
|
1037
|
+
totalPages: Math.ceil(total / limit)
|
|
1038
|
+
},
|
|
1039
|
+
columns: extractSchemaMeta(model, config.createSchema)
|
|
1040
|
+
};
|
|
1041
|
+
next();
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
next(error);
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
var parseBulkDelete = (req, res, next) => {
|
|
1048
|
+
try {
|
|
1049
|
+
const bulkReq = req;
|
|
1050
|
+
let ids = [];
|
|
1051
|
+
if (Array.isArray(req.body)) {
|
|
1052
|
+
ids = req.body;
|
|
1053
|
+
} else if (req.body && Array.isArray(req.body.ids)) {
|
|
1054
|
+
ids = req.body.ids;
|
|
1055
|
+
} else if (req.body && typeof req.body === "object") {
|
|
1056
|
+
if (Array.isArray(req.body.data)) {
|
|
1057
|
+
ids = req.body.data;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
if (ids.length === 0) {
|
|
1061
|
+
return badRequestResponse(
|
|
1062
|
+
res,
|
|
1063
|
+
'Request body must contain an array of IDs. Use ["*"] to delete all records or ["id1", "id2"] to delete specific records.'
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
if (ids.length === 1 && ids[0] === "*") {
|
|
1067
|
+
bulkReq.deleteAll = true;
|
|
1068
|
+
bulkReq.deleteIds = [];
|
|
1069
|
+
logger.info("Bulk delete: Deleting all records");
|
|
1070
|
+
return next();
|
|
1071
|
+
}
|
|
1072
|
+
const validIds = [];
|
|
1073
|
+
const invalidIds = [];
|
|
1074
|
+
for (const id of ids) {
|
|
1075
|
+
if (typeof id === "string" && Types.ObjectId.isValid(id)) {
|
|
1076
|
+
validIds.push(id);
|
|
1077
|
+
} else {
|
|
1078
|
+
invalidIds.push(id);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (invalidIds.length > 0) {
|
|
1082
|
+
return badRequestResponse(
|
|
1083
|
+
res,
|
|
1084
|
+
`Invalid ID format(s): ${invalidIds.slice(0, 5).join(", ")}${invalidIds.length > 5 ? "..." : ""}. All IDs must be valid MongoDB ObjectIds.`
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
if (validIds.length === 0) {
|
|
1088
|
+
return badRequestResponse(res, "No valid IDs provided for deletion.");
|
|
1089
|
+
}
|
|
1090
|
+
bulkReq.deleteAll = false;
|
|
1091
|
+
bulkReq.deleteIds = validIds;
|
|
1092
|
+
logger.info(`Bulk delete: Deleting ${validIds.length} record(s)`);
|
|
1093
|
+
next();
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
logger.error("Error in parseBulkDelete middleware", error);
|
|
1096
|
+
return badRequestResponse(res, "Failed to parse delete request");
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
var buildDeleteFilter = (req, organizationId) => {
|
|
1100
|
+
const filter = {
|
|
1101
|
+
organizationId: new Types.ObjectId(organizationId)
|
|
1102
|
+
};
|
|
1103
|
+
if (!req.deleteAll && req.deleteIds && req.deleteIds.length > 0) {
|
|
1104
|
+
filter._id = {
|
|
1105
|
+
$in: req.deleteIds.map((id) => new Types.ObjectId(id))
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return filter;
|
|
1109
|
+
};
|
|
1110
|
+
var createBulkDeleteHandler = (Model2, modelName) => {
|
|
1111
|
+
return async (req, res) => {
|
|
1112
|
+
const bulkReq = req;
|
|
1113
|
+
const organizationId = req.headers["x-organization-id"];
|
|
1114
|
+
if (!organizationId) {
|
|
1115
|
+
return badRequestResponse(res, "Organization ID is required");
|
|
1116
|
+
}
|
|
1117
|
+
try {
|
|
1118
|
+
const filter = buildDeleteFilter(bulkReq, organizationId);
|
|
1119
|
+
const result = await Model2.deleteMany(filter);
|
|
1120
|
+
const deletedCount = result.deletedCount || 0;
|
|
1121
|
+
logger.info(`Bulk delete completed: ${deletedCount} ${modelName}(s) deleted`, {
|
|
1122
|
+
organizationId,
|
|
1123
|
+
deleteAll: bulkReq.deleteAll,
|
|
1124
|
+
requestedIds: bulkReq.deleteIds?.length || "all",
|
|
1125
|
+
deletedCount
|
|
1126
|
+
});
|
|
1127
|
+
return res.status(200).json({
|
|
1128
|
+
message: `Successfully deleted ${deletedCount} ${modelName}(s)`,
|
|
1129
|
+
data: {
|
|
1130
|
+
deletedCount,
|
|
1131
|
+
deleteAll: bulkReq.deleteAll
|
|
1132
|
+
},
|
|
1133
|
+
status: "success",
|
|
1134
|
+
statusCode: 200
|
|
1135
|
+
});
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
logger.error(`Error in bulk delete ${modelName}`, error);
|
|
1138
|
+
return res.status(500).json({
|
|
1139
|
+
message: `Failed to delete ${modelName}(s)`,
|
|
1140
|
+
data: null,
|
|
1141
|
+
status: "error",
|
|
1142
|
+
statusCode: 500
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
};
|
|
1147
|
+
|
|
479
1148
|
// src/server/utils/filter-builder.ts
|
|
480
1149
|
var buildFilter = (options) => {
|
|
481
1150
|
const {
|
|
@@ -770,45 +1439,704 @@ var packageCheckServer = {
|
|
|
770
1439
|
print: printPackageCheckSummary
|
|
771
1440
|
};
|
|
772
1441
|
|
|
1442
|
+
// src/server/configs/cors.config.ts
|
|
1443
|
+
var DEFAULT_CORS_CONFIG = {
|
|
1444
|
+
productionOrigins: [],
|
|
1445
|
+
developmentOrigins: [
|
|
1446
|
+
"http://localhost:3000",
|
|
1447
|
+
"http://localhost:4000",
|
|
1448
|
+
"http://localhost:5000",
|
|
1449
|
+
"http://localhost:5173",
|
|
1450
|
+
"http://localhost:8080",
|
|
1451
|
+
"http://127.0.0.1:3000",
|
|
1452
|
+
"http://127.0.0.1:4000",
|
|
1453
|
+
"http://127.0.0.1:5000",
|
|
1454
|
+
"http://127.0.0.1:5173",
|
|
1455
|
+
"http://127.0.0.1:8080"
|
|
1456
|
+
],
|
|
1457
|
+
allowedSubdomains: [],
|
|
1458
|
+
originPatterns: [],
|
|
1459
|
+
allowNoOrigin: true,
|
|
1460
|
+
allowAllInDev: true,
|
|
1461
|
+
credentials: true,
|
|
1462
|
+
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"],
|
|
1463
|
+
allowedHeaders: [
|
|
1464
|
+
"Content-Type",
|
|
1465
|
+
"Authorization",
|
|
1466
|
+
"X-Requested-With",
|
|
1467
|
+
"Accept",
|
|
1468
|
+
"Origin",
|
|
1469
|
+
"X-API-Key",
|
|
1470
|
+
"X-Organization-Id",
|
|
1471
|
+
"X-Request-Id"
|
|
1472
|
+
],
|
|
1473
|
+
exposedHeaders: [
|
|
1474
|
+
"Content-Range",
|
|
1475
|
+
"X-Content-Range",
|
|
1476
|
+
"X-Total-Count",
|
|
1477
|
+
"X-Request-Id"
|
|
1478
|
+
],
|
|
1479
|
+
maxAge: 86400
|
|
1480
|
+
// 24 hours
|
|
1481
|
+
};
|
|
1482
|
+
var createCorsOptions = (config = {}) => {
|
|
1483
|
+
const finalConfig = { ...DEFAULT_CORS_CONFIG, ...config };
|
|
1484
|
+
const {
|
|
1485
|
+
productionOrigins,
|
|
1486
|
+
developmentOrigins,
|
|
1487
|
+
allowedSubdomains,
|
|
1488
|
+
originPatterns,
|
|
1489
|
+
allowNoOrigin,
|
|
1490
|
+
allowAllInDev,
|
|
1491
|
+
customValidator,
|
|
1492
|
+
credentials,
|
|
1493
|
+
methods,
|
|
1494
|
+
allowedHeaders,
|
|
1495
|
+
exposedHeaders,
|
|
1496
|
+
maxAge
|
|
1497
|
+
} = finalConfig;
|
|
1498
|
+
const allOrigins = /* @__PURE__ */ new Set([...productionOrigins, ...developmentOrigins]);
|
|
1499
|
+
const originHandler = (origin, callback) => {
|
|
1500
|
+
if (!origin) {
|
|
1501
|
+
callback(null, allowNoOrigin);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
if (allOrigins.has(origin)) {
|
|
1505
|
+
callback(null, true);
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
if (allowedSubdomains.some((subdomain) => origin.endsWith(subdomain))) {
|
|
1509
|
+
callback(null, true);
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
if (originPatterns.some((pattern) => pattern.test(origin))) {
|
|
1513
|
+
callback(null, true);
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
if (customValidator && customValidator(origin)) {
|
|
1517
|
+
callback(null, true);
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
if (process.env.NODE_ENV !== "production" && allowAllInDev) {
|
|
1521
|
+
callback(null, true);
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (process.env.NODE_ENV === "production") {
|
|
1525
|
+
callback(new Error(`Origin ${origin} not allowed by CORS`));
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
callback(null, true);
|
|
1529
|
+
};
|
|
1530
|
+
return {
|
|
1531
|
+
origin: originHandler,
|
|
1532
|
+
credentials,
|
|
1533
|
+
methods,
|
|
1534
|
+
allowedHeaders,
|
|
1535
|
+
exposedHeaders,
|
|
1536
|
+
maxAge
|
|
1537
|
+
};
|
|
1538
|
+
};
|
|
1539
|
+
var createBrandCorsOptions = (brandDomain, additionalConfig = {}) => {
|
|
1540
|
+
const productionOrigins = [
|
|
1541
|
+
`https://${brandDomain}`,
|
|
1542
|
+
`https://www.${brandDomain}`
|
|
1543
|
+
];
|
|
1544
|
+
const allowedSubdomains = [`.${brandDomain}`];
|
|
1545
|
+
return createCorsOptions({
|
|
1546
|
+
productionOrigins,
|
|
1547
|
+
allowedSubdomains,
|
|
1548
|
+
...additionalConfig
|
|
1549
|
+
});
|
|
1550
|
+
};
|
|
1551
|
+
var createMultiBrandCorsOptions = (domains, additionalConfig = {}) => {
|
|
1552
|
+
const productionOrigins = domains.flatMap((domain) => [
|
|
1553
|
+
`https://${domain}`,
|
|
1554
|
+
`https://www.${domain}`
|
|
1555
|
+
]);
|
|
1556
|
+
const allowedSubdomains = domains.map((domain) => `.${domain}`);
|
|
1557
|
+
return createCorsOptions({
|
|
1558
|
+
productionOrigins,
|
|
1559
|
+
allowedSubdomains,
|
|
1560
|
+
...additionalConfig
|
|
1561
|
+
});
|
|
1562
|
+
};
|
|
1563
|
+
var EXYCONN_CORS_CONFIG = {
|
|
1564
|
+
productionOrigins: [
|
|
1565
|
+
"https://exyconn.com",
|
|
1566
|
+
"https://www.exyconn.com",
|
|
1567
|
+
"https://botify.life",
|
|
1568
|
+
"https://www.botify.life",
|
|
1569
|
+
"https://partywings.fun",
|
|
1570
|
+
"https://www.partywings.fun",
|
|
1571
|
+
"https://sibera.work",
|
|
1572
|
+
"https://www.sibera.work",
|
|
1573
|
+
"https://spentiva.com",
|
|
1574
|
+
"https://www.spentiva.com"
|
|
1575
|
+
],
|
|
1576
|
+
allowedSubdomains: [
|
|
1577
|
+
".exyconn.com",
|
|
1578
|
+
".botify.life",
|
|
1579
|
+
".partywings.fun",
|
|
1580
|
+
".sibera.work",
|
|
1581
|
+
".spentiva.com"
|
|
1582
|
+
],
|
|
1583
|
+
developmentOrigins: [
|
|
1584
|
+
"http://localhost:3000",
|
|
1585
|
+
"http://localhost:4000",
|
|
1586
|
+
"http://localhost:4001",
|
|
1587
|
+
"http://localhost:4002",
|
|
1588
|
+
"http://localhost:4003",
|
|
1589
|
+
"http://localhost:4004",
|
|
1590
|
+
"http://localhost:4005",
|
|
1591
|
+
"http://localhost:5173",
|
|
1592
|
+
"http://127.0.0.1:3000",
|
|
1593
|
+
"http://127.0.0.1:4000",
|
|
1594
|
+
"http://127.0.0.1:5173"
|
|
1595
|
+
]
|
|
1596
|
+
};
|
|
1597
|
+
var STRICT_CORS_CONFIG = {
|
|
1598
|
+
allowNoOrigin: false,
|
|
1599
|
+
allowAllInDev: false,
|
|
1600
|
+
methods: ["GET", "POST", "PUT", "DELETE"]
|
|
1601
|
+
};
|
|
1602
|
+
var PERMISSIVE_CORS_CONFIG = {
|
|
1603
|
+
allowNoOrigin: true,
|
|
1604
|
+
allowAllInDev: true,
|
|
1605
|
+
originPatterns: [/localhost/, /127\.0\.0\.1/]
|
|
1606
|
+
};
|
|
1607
|
+
var corsOptions = createCorsOptions(EXYCONN_CORS_CONFIG);
|
|
1608
|
+
var DEFAULT_RATE_LIMIT_TIERS = {
|
|
1609
|
+
STANDARD: {
|
|
1610
|
+
windowMs: 15 * 60 * 1e3,
|
|
1611
|
+
// 15 minutes
|
|
1612
|
+
maxRequests: 100,
|
|
1613
|
+
message: "Too many requests, please try again later.",
|
|
1614
|
+
skipSuccessfulRequests: false,
|
|
1615
|
+
skipFailedRequests: false
|
|
1616
|
+
},
|
|
1617
|
+
STRICT: {
|
|
1618
|
+
windowMs: 15 * 60 * 1e3,
|
|
1619
|
+
// 15 minutes
|
|
1620
|
+
maxRequests: 20,
|
|
1621
|
+
message: "Too many requests, please try again later.",
|
|
1622
|
+
skipSuccessfulRequests: false,
|
|
1623
|
+
skipFailedRequests: false
|
|
1624
|
+
},
|
|
1625
|
+
DDOS: {
|
|
1626
|
+
windowMs: 60 * 1e3,
|
|
1627
|
+
// 1 minute
|
|
1628
|
+
maxRequests: 60,
|
|
1629
|
+
message: "Rate limit exceeded. Please slow down.",
|
|
1630
|
+
skipSuccessfulRequests: false,
|
|
1631
|
+
skipFailedRequests: false
|
|
1632
|
+
},
|
|
1633
|
+
// Additional presets
|
|
1634
|
+
VERY_STRICT: {
|
|
1635
|
+
windowMs: 60 * 60 * 1e3,
|
|
1636
|
+
// 1 hour
|
|
1637
|
+
maxRequests: 5,
|
|
1638
|
+
message: "Too many attempts. Please try again in an hour.",
|
|
1639
|
+
skipSuccessfulRequests: false,
|
|
1640
|
+
skipFailedRequests: false
|
|
1641
|
+
},
|
|
1642
|
+
RELAXED: {
|
|
1643
|
+
windowMs: 15 * 60 * 1e3,
|
|
1644
|
+
// 15 minutes
|
|
1645
|
+
maxRequests: 500,
|
|
1646
|
+
message: "Rate limit exceeded.",
|
|
1647
|
+
skipSuccessfulRequests: false,
|
|
1648
|
+
skipFailedRequests: false
|
|
1649
|
+
},
|
|
1650
|
+
API: {
|
|
1651
|
+
windowMs: 60 * 1e3,
|
|
1652
|
+
// 1 minute
|
|
1653
|
+
maxRequests: 30,
|
|
1654
|
+
message: "API rate limit exceeded.",
|
|
1655
|
+
skipSuccessfulRequests: false,
|
|
1656
|
+
skipFailedRequests: false
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
var defaultKeyGenerator = (req) => {
|
|
1660
|
+
const forwarded = req.headers["x-forwarded-for"];
|
|
1661
|
+
const ip = forwarded ? Array.isArray(forwarded) ? forwarded[0] : forwarded.split(",")[0].trim() : req.ip || req.socket.remoteAddress || "unknown";
|
|
1662
|
+
return ip;
|
|
1663
|
+
};
|
|
1664
|
+
var createPrefixedKeyGenerator = (prefix) => (req) => {
|
|
1665
|
+
return `${prefix}:${defaultKeyGenerator(req)}`;
|
|
1666
|
+
};
|
|
1667
|
+
var createUserKeyGenerator = (getUserId) => (req) => {
|
|
1668
|
+
const userId = getUserId(req);
|
|
1669
|
+
return userId || defaultKeyGenerator(req);
|
|
1670
|
+
};
|
|
1671
|
+
var createApiKeyGenerator = (headerName = "x-api-key") => (req) => {
|
|
1672
|
+
const apiKey = req.headers[headerName.toLowerCase()];
|
|
1673
|
+
return apiKey || defaultKeyGenerator(req);
|
|
1674
|
+
};
|
|
1675
|
+
var createRateLimitResponse = (message, retryAfter) => ({
|
|
1676
|
+
status: "error",
|
|
1677
|
+
statusCode: 429,
|
|
1678
|
+
message,
|
|
1679
|
+
...retryAfter
|
|
1680
|
+
});
|
|
1681
|
+
var createRateLimiter = (tierConfig, options = {}) => {
|
|
1682
|
+
const {
|
|
1683
|
+
standardHeaders = true,
|
|
1684
|
+
legacyHeaders = false,
|
|
1685
|
+
keyGenerator = defaultKeyGenerator,
|
|
1686
|
+
skip,
|
|
1687
|
+
handler
|
|
1688
|
+
} = options;
|
|
1689
|
+
return rateLimit({
|
|
1690
|
+
windowMs: tierConfig.windowMs,
|
|
1691
|
+
max: tierConfig.maxRequests,
|
|
1692
|
+
message: createRateLimitResponse(tierConfig.message),
|
|
1693
|
+
standardHeaders,
|
|
1694
|
+
legacyHeaders,
|
|
1695
|
+
keyGenerator,
|
|
1696
|
+
skip,
|
|
1697
|
+
handler,
|
|
1698
|
+
skipSuccessfulRequests: tierConfig.skipSuccessfulRequests,
|
|
1699
|
+
skipFailedRequests: tierConfig.skipFailedRequests
|
|
1700
|
+
});
|
|
1701
|
+
};
|
|
1702
|
+
var createStandardRateLimiter = (config = {}, options = {}) => {
|
|
1703
|
+
const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.STANDARD, ...config };
|
|
1704
|
+
return createRateLimiter(tierConfig, options);
|
|
1705
|
+
};
|
|
1706
|
+
var createStrictRateLimiter = (config = {}, options = {}) => {
|
|
1707
|
+
const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.STRICT, ...config };
|
|
1708
|
+
return createRateLimiter(tierConfig, options);
|
|
1709
|
+
};
|
|
1710
|
+
var createDdosRateLimiter = (config = {}, options = {}) => {
|
|
1711
|
+
const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.DDOS, ...config };
|
|
1712
|
+
return createRateLimiter(tierConfig, options);
|
|
1713
|
+
};
|
|
1714
|
+
var createApiRateLimiter = (config = {}, options = {}) => {
|
|
1715
|
+
const tierConfig = { ...DEFAULT_RATE_LIMIT_TIERS.API, ...config };
|
|
1716
|
+
return createRateLimiter(tierConfig, {
|
|
1717
|
+
keyGenerator: createApiKeyGenerator(),
|
|
1718
|
+
...options
|
|
1719
|
+
});
|
|
1720
|
+
};
|
|
1721
|
+
var RateLimiterBuilder = class {
|
|
1722
|
+
constructor(preset = "STANDARD") {
|
|
1723
|
+
const presetConfig = DEFAULT_RATE_LIMIT_TIERS[preset];
|
|
1724
|
+
this.config = {
|
|
1725
|
+
windowMs: presetConfig.windowMs,
|
|
1726
|
+
maxRequests: presetConfig.maxRequests,
|
|
1727
|
+
message: presetConfig.message,
|
|
1728
|
+
skipSuccessfulRequests: presetConfig.skipSuccessfulRequests ?? false,
|
|
1729
|
+
skipFailedRequests: presetConfig.skipFailedRequests ?? false
|
|
1730
|
+
};
|
|
1731
|
+
this.options = {};
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Set window duration
|
|
1735
|
+
*/
|
|
1736
|
+
windowMs(ms) {
|
|
1737
|
+
this.config.windowMs = ms;
|
|
1738
|
+
return this;
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Set window duration in minutes
|
|
1742
|
+
*/
|
|
1743
|
+
windowMinutes(minutes) {
|
|
1744
|
+
this.config.windowMs = minutes * 60 * 1e3;
|
|
1745
|
+
return this;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Set window duration in hours
|
|
1749
|
+
*/
|
|
1750
|
+
windowHours(hours) {
|
|
1751
|
+
this.config.windowMs = hours * 60 * 60 * 1e3;
|
|
1752
|
+
return this;
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Set maximum requests
|
|
1756
|
+
*/
|
|
1757
|
+
max(requests) {
|
|
1758
|
+
this.config.maxRequests = requests;
|
|
1759
|
+
return this;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Set error message
|
|
1763
|
+
*/
|
|
1764
|
+
message(msg) {
|
|
1765
|
+
this.config.message = msg;
|
|
1766
|
+
return this;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Skip successful requests
|
|
1770
|
+
*/
|
|
1771
|
+
skipSuccessful(skip = true) {
|
|
1772
|
+
this.config.skipSuccessfulRequests = skip;
|
|
1773
|
+
return this;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Skip failed requests
|
|
1777
|
+
*/
|
|
1778
|
+
skipFailed(skip = true) {
|
|
1779
|
+
this.config.skipFailedRequests = skip;
|
|
1780
|
+
return this;
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Set key generator
|
|
1784
|
+
*/
|
|
1785
|
+
keyBy(generator) {
|
|
1786
|
+
this.options.keyGenerator = generator;
|
|
1787
|
+
return this;
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Key by IP (default)
|
|
1791
|
+
*/
|
|
1792
|
+
keyByIp() {
|
|
1793
|
+
this.options.keyGenerator = defaultKeyGenerator;
|
|
1794
|
+
return this;
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Key by API key
|
|
1798
|
+
*/
|
|
1799
|
+
keyByApiKey(headerName) {
|
|
1800
|
+
this.options.keyGenerator = createApiKeyGenerator(headerName);
|
|
1801
|
+
return this;
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Skip certain requests
|
|
1805
|
+
*/
|
|
1806
|
+
skipWhen(predicate) {
|
|
1807
|
+
this.options.skip = predicate;
|
|
1808
|
+
return this;
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Build the rate limiter
|
|
1812
|
+
*/
|
|
1813
|
+
build() {
|
|
1814
|
+
return createRateLimiter(this.config, this.options);
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
var rateLimiter = (preset) => {
|
|
1818
|
+
return new RateLimiterBuilder(preset);
|
|
1819
|
+
};
|
|
1820
|
+
var RATE_LIMIT_CONFIG = {
|
|
1821
|
+
STANDARD: DEFAULT_RATE_LIMIT_TIERS.STANDARD,
|
|
1822
|
+
STRICT: DEFAULT_RATE_LIMIT_TIERS.STRICT,
|
|
1823
|
+
DDOS: DEFAULT_RATE_LIMIT_TIERS.DDOS
|
|
1824
|
+
};
|
|
1825
|
+
var standardRateLimiter = createStandardRateLimiter();
|
|
1826
|
+
var strictRateLimiter = createStrictRateLimiter();
|
|
1827
|
+
var ddosProtectionLimiter = createDdosRateLimiter();
|
|
1828
|
+
|
|
1829
|
+
// src/server/configs/server.config.ts
|
|
1830
|
+
var DEFAULT_SERVER_CONFIG = {
|
|
1831
|
+
name: "app-server",
|
|
1832
|
+
version: "1.0.0",
|
|
1833
|
+
environment: process.env.NODE_ENV || "development",
|
|
1834
|
+
port: parseInt(process.env.PORT || "3000", 10),
|
|
1835
|
+
host: process.env.HOST || "0.0.0.0",
|
|
1836
|
+
basePath: "/api",
|
|
1837
|
+
debug: process.env.DEBUG === "true",
|
|
1838
|
+
trustProxy: true
|
|
1839
|
+
};
|
|
1840
|
+
var DEFAULT_DATABASE_CONFIG = {
|
|
1841
|
+
uri: process.env.DATABASE_URL || process.env.MONGODB_URI || "",
|
|
1842
|
+
name: process.env.DATABASE_NAME || "app_db",
|
|
1843
|
+
maxPoolSize: process.env.NODE_ENV === "production" ? 50 : 10,
|
|
1844
|
+
minPoolSize: process.env.NODE_ENV === "production" ? 10 : 5,
|
|
1845
|
+
socketTimeoutMS: 45e3,
|
|
1846
|
+
serverSelectionTimeoutMS: 1e4,
|
|
1847
|
+
maxIdleTimeMS: 1e4,
|
|
1848
|
+
retryWrites: true,
|
|
1849
|
+
retryReads: true,
|
|
1850
|
+
writeConcern: "majority"
|
|
1851
|
+
};
|
|
1852
|
+
var DEFAULT_AUTH_CONFIG = {
|
|
1853
|
+
jwtSecret: process.env.JWT_SECRET || "",
|
|
1854
|
+
jwtExpiresIn: process.env.JWT_EXPIRES_IN || "7d",
|
|
1855
|
+
refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || "30d",
|
|
1856
|
+
enableRefreshTokens: true,
|
|
1857
|
+
apiKeyHeader: "x-api-key",
|
|
1858
|
+
orgHeader: "x-organization-id"
|
|
1859
|
+
};
|
|
1860
|
+
var DEFAULT_LOGGING_CONFIG = {
|
|
1861
|
+
level: process.env.LOG_LEVEL || "info",
|
|
1862
|
+
logsDir: process.env.LOGS_DIR || "logs",
|
|
1863
|
+
maxSize: "20m",
|
|
1864
|
+
maxFiles: "14d",
|
|
1865
|
+
errorMaxFiles: "30d",
|
|
1866
|
+
console: true,
|
|
1867
|
+
file: process.env.NODE_ENV === "production",
|
|
1868
|
+
json: process.env.NODE_ENV === "production"
|
|
1869
|
+
};
|
|
1870
|
+
var DEFAULT_CORS_ORIGINS = {
|
|
1871
|
+
production: [],
|
|
1872
|
+
development: [
|
|
1873
|
+
"http://localhost:3000",
|
|
1874
|
+
"http://localhost:4000",
|
|
1875
|
+
"http://localhost:5173",
|
|
1876
|
+
"http://127.0.0.1:3000",
|
|
1877
|
+
"http://127.0.0.1:4000",
|
|
1878
|
+
"http://127.0.0.1:5173"
|
|
1879
|
+
],
|
|
1880
|
+
patterns: []
|
|
1881
|
+
};
|
|
1882
|
+
var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
1883
|
+
enabled: true,
|
|
1884
|
+
standard: {
|
|
1885
|
+
windowMs: 15 * 60 * 1e3,
|
|
1886
|
+
// 15 minutes
|
|
1887
|
+
maxRequests: 100,
|
|
1888
|
+
message: "Too many requests, please try again later."
|
|
1889
|
+
},
|
|
1890
|
+
strict: {
|
|
1891
|
+
windowMs: 15 * 60 * 1e3,
|
|
1892
|
+
// 15 minutes
|
|
1893
|
+
maxRequests: 20,
|
|
1894
|
+
message: "Too many requests, please try again later."
|
|
1895
|
+
},
|
|
1896
|
+
ddos: {
|
|
1897
|
+
windowMs: 60 * 1e3,
|
|
1898
|
+
// 1 minute
|
|
1899
|
+
maxRequests: 60,
|
|
1900
|
+
message: "Rate limit exceeded. Please slow down.",
|
|
1901
|
+
skipSuccessfulRequests: false
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
function deepMerge(target, source) {
|
|
1905
|
+
const result = { ...target };
|
|
1906
|
+
for (const key in source) {
|
|
1907
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
1908
|
+
const sourceValue = source[key];
|
|
1909
|
+
const targetValue = target[key];
|
|
1910
|
+
if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) {
|
|
1911
|
+
result[key] = deepMerge(
|
|
1912
|
+
targetValue,
|
|
1913
|
+
sourceValue
|
|
1914
|
+
);
|
|
1915
|
+
} else if (sourceValue !== void 0) {
|
|
1916
|
+
result[key] = sourceValue;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return result;
|
|
1921
|
+
}
|
|
1922
|
+
var ConfigBuilder = class {
|
|
1923
|
+
constructor() {
|
|
1924
|
+
this.config = {
|
|
1925
|
+
server: { ...DEFAULT_SERVER_CONFIG },
|
|
1926
|
+
database: { ...DEFAULT_DATABASE_CONFIG },
|
|
1927
|
+
auth: { ...DEFAULT_AUTH_CONFIG },
|
|
1928
|
+
logging: { ...DEFAULT_LOGGING_CONFIG },
|
|
1929
|
+
cors: { ...DEFAULT_CORS_ORIGINS },
|
|
1930
|
+
rateLimit: { ...DEFAULT_RATE_LIMIT_CONFIG }
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Set server configuration
|
|
1935
|
+
*/
|
|
1936
|
+
setServer(config) {
|
|
1937
|
+
this.config.server = deepMerge(this.config.server, config);
|
|
1938
|
+
return this;
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Set database configuration
|
|
1942
|
+
*/
|
|
1943
|
+
setDatabase(config) {
|
|
1944
|
+
this.config.database = deepMerge(this.config.database, config);
|
|
1945
|
+
return this;
|
|
1946
|
+
}
|
|
1947
|
+
/**
|
|
1948
|
+
* Set auth configuration
|
|
1949
|
+
*/
|
|
1950
|
+
setAuth(config) {
|
|
1951
|
+
this.config.auth = deepMerge(this.config.auth, config);
|
|
1952
|
+
return this;
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Set logging configuration
|
|
1956
|
+
*/
|
|
1957
|
+
setLogging(config) {
|
|
1958
|
+
this.config.logging = deepMerge(this.config.logging, config);
|
|
1959
|
+
return this;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Set CORS origins
|
|
1963
|
+
*/
|
|
1964
|
+
setCorsOrigins(config) {
|
|
1965
|
+
this.config.cors = deepMerge(this.config.cors, config);
|
|
1966
|
+
return this;
|
|
1967
|
+
}
|
|
1968
|
+
/**
|
|
1969
|
+
* Add CORS production origin
|
|
1970
|
+
*/
|
|
1971
|
+
addProductionOrigin(origin) {
|
|
1972
|
+
if (!this.config.cors.production.includes(origin)) {
|
|
1973
|
+
this.config.cors.production.push(origin);
|
|
1974
|
+
}
|
|
1975
|
+
return this;
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Add CORS development origin
|
|
1979
|
+
*/
|
|
1980
|
+
addDevelopmentOrigin(origin) {
|
|
1981
|
+
if (!this.config.cors.development.includes(origin)) {
|
|
1982
|
+
this.config.cors.development.push(origin);
|
|
1983
|
+
}
|
|
1984
|
+
return this;
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Add CORS pattern
|
|
1988
|
+
*/
|
|
1989
|
+
addCorsPattern(pattern) {
|
|
1990
|
+
if (!this.config.cors.patterns.includes(pattern)) {
|
|
1991
|
+
this.config.cors.patterns.push(pattern);
|
|
1992
|
+
}
|
|
1993
|
+
return this;
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Set rate limit configuration
|
|
1997
|
+
*/
|
|
1998
|
+
setRateLimit(config) {
|
|
1999
|
+
this.config.rateLimit = deepMerge(this.config.rateLimit, config);
|
|
2000
|
+
return this;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Add custom rate limit tier
|
|
2004
|
+
*/
|
|
2005
|
+
addRateLimitTier(name, tier) {
|
|
2006
|
+
if (!this.config.rateLimit.custom) {
|
|
2007
|
+
this.config.rateLimit.custom = {};
|
|
2008
|
+
}
|
|
2009
|
+
this.config.rateLimit.custom[name] = tier;
|
|
2010
|
+
return this;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Set custom configuration
|
|
2014
|
+
*/
|
|
2015
|
+
setCustom(key, value) {
|
|
2016
|
+
if (!this.config.custom) {
|
|
2017
|
+
this.config.custom = {};
|
|
2018
|
+
}
|
|
2019
|
+
this.config.custom[key] = value;
|
|
2020
|
+
return this;
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Load configuration from environment variables
|
|
2024
|
+
*/
|
|
2025
|
+
loadFromEnv() {
|
|
2026
|
+
if (process.env.SERVER_NAME) this.config.server.name = process.env.SERVER_NAME;
|
|
2027
|
+
if (process.env.SERVER_VERSION) this.config.server.version = process.env.SERVER_VERSION;
|
|
2028
|
+
if (process.env.PORT) this.config.server.port = parseInt(process.env.PORT, 10);
|
|
2029
|
+
if (process.env.HOST) this.config.server.host = process.env.HOST;
|
|
2030
|
+
if (process.env.BASE_PATH) this.config.server.basePath = process.env.BASE_PATH;
|
|
2031
|
+
if (process.env.DATABASE_URL) this.config.database.uri = process.env.DATABASE_URL;
|
|
2032
|
+
if (process.env.MONGODB_URI) this.config.database.uri = process.env.MONGODB_URI;
|
|
2033
|
+
if (process.env.DATABASE_NAME) this.config.database.name = process.env.DATABASE_NAME;
|
|
2034
|
+
if (process.env.JWT_SECRET) this.config.auth.jwtSecret = process.env.JWT_SECRET;
|
|
2035
|
+
if (process.env.JWT_EXPIRES_IN) this.config.auth.jwtExpiresIn = process.env.JWT_EXPIRES_IN;
|
|
2036
|
+
if (process.env.LOG_LEVEL) this.config.logging.level = process.env.LOG_LEVEL;
|
|
2037
|
+
if (process.env.LOGS_DIR) this.config.logging.logsDir = process.env.LOGS_DIR;
|
|
2038
|
+
if (process.env.CORS_ORIGINS) {
|
|
2039
|
+
const origins = process.env.CORS_ORIGINS.split(",").map((o) => o.trim());
|
|
2040
|
+
this.config.cors.production.push(...origins);
|
|
2041
|
+
}
|
|
2042
|
+
return this;
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Validate configuration
|
|
2046
|
+
*/
|
|
2047
|
+
validate() {
|
|
2048
|
+
const errors = [];
|
|
2049
|
+
if (!this.config.server.name) errors.push("Server name is required");
|
|
2050
|
+
if (this.config.server.port < 1 || this.config.server.port > 65535) {
|
|
2051
|
+
errors.push("Server port must be between 1 and 65535");
|
|
2052
|
+
}
|
|
2053
|
+
if (this.config.server.environment === "production") {
|
|
2054
|
+
if (!this.config.auth.jwtSecret || this.config.auth.jwtSecret.length < 32) {
|
|
2055
|
+
errors.push("JWT secret must be at least 32 characters in production");
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
return { valid: errors.length === 0, errors };
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Build the final configuration
|
|
2062
|
+
*/
|
|
2063
|
+
build() {
|
|
2064
|
+
return { ...this.config };
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
var createConfig = () => {
|
|
2068
|
+
return new ConfigBuilder();
|
|
2069
|
+
};
|
|
2070
|
+
var buildConfig = (partial = {}) => {
|
|
2071
|
+
const builder = createConfig().loadFromEnv();
|
|
2072
|
+
if (partial.server) builder.setServer(partial.server);
|
|
2073
|
+
if (partial.database) builder.setDatabase(partial.database);
|
|
2074
|
+
if (partial.auth) builder.setAuth(partial.auth);
|
|
2075
|
+
if (partial.logging) builder.setLogging(partial.logging);
|
|
2076
|
+
if (partial.cors) builder.setCorsOrigins(partial.cors);
|
|
2077
|
+
if (partial.rateLimit) builder.setRateLimit(partial.rateLimit);
|
|
2078
|
+
return builder.build();
|
|
2079
|
+
};
|
|
2080
|
+
var isProduction = (config) => {
|
|
2081
|
+
return (config?.environment || process.env.NODE_ENV) === "production";
|
|
2082
|
+
};
|
|
2083
|
+
var isDevelopment = (config) => {
|
|
2084
|
+
return (config?.environment || process.env.NODE_ENV) === "development";
|
|
2085
|
+
};
|
|
2086
|
+
var isTest = (config) => {
|
|
2087
|
+
return (config?.environment || process.env.NODE_ENV) === "test";
|
|
2088
|
+
};
|
|
2089
|
+
var getDatabaseOptions = (config) => {
|
|
2090
|
+
return {
|
|
2091
|
+
maxPoolSize: config.maxPoolSize,
|
|
2092
|
+
minPoolSize: config.minPoolSize,
|
|
2093
|
+
socketTimeoutMS: config.socketTimeoutMS,
|
|
2094
|
+
serverSelectionTimeoutMS: config.serverSelectionTimeoutMS,
|
|
2095
|
+
maxIdleTimeMS: config.maxIdleTimeMS,
|
|
2096
|
+
retryWrites: config.retryWrites,
|
|
2097
|
+
retryReads: config.retryReads,
|
|
2098
|
+
w: config.writeConcern
|
|
2099
|
+
};
|
|
2100
|
+
};
|
|
2101
|
+
|
|
773
2102
|
// src/client/index.ts
|
|
774
2103
|
var client_exports = {};
|
|
775
2104
|
__export(client_exports, {
|
|
776
|
-
|
|
2105
|
+
API_BASE_URL: () => API_BASE_URL,
|
|
2106
|
+
API_PREFIX: () => API_PREFIX,
|
|
777
2107
|
ClientLogger: () => ClientLogger,
|
|
778
2108
|
ContactForm: () => ContactForm,
|
|
779
|
-
|
|
2109
|
+
ERROR_CODES: () => ERROR_CODES,
|
|
780
2110
|
LoginForm: () => LoginForm,
|
|
781
2111
|
NewsletterForm: () => NewsletterForm,
|
|
782
2112
|
RegisterForm: () => RegisterForm,
|
|
2113
|
+
STATUS_CODES: () => STATUS_CODES,
|
|
2114
|
+
STATUS_MESSAGES: () => STATUS_MESSAGES,
|
|
2115
|
+
SUCCESS_CODES: () => SUCCESS_CODES,
|
|
783
2116
|
ThemeContext: () => ThemeContext,
|
|
784
2117
|
ThemeProvider: () => ThemeProvider,
|
|
785
2118
|
ThemeToggle: () => ThemeToggle,
|
|
786
2119
|
VALIDATION_MESSAGES: () => VALIDATION_MESSAGES,
|
|
787
|
-
addDays: () => addDays,
|
|
788
2120
|
adjustColor: () => adjustColor,
|
|
789
|
-
|
|
2121
|
+
axios: () => axiosInstance,
|
|
790
2122
|
camelToKebab: () => camelToKebab,
|
|
791
2123
|
capitalize: () => capitalize,
|
|
792
2124
|
capitalizeWords: () => capitalizeWords,
|
|
793
|
-
checkPackage: () => checkPackage,
|
|
794
2125
|
clientLogger: () => clientLogger,
|
|
795
2126
|
contactFormSchema: () => contactFormSchema,
|
|
796
2127
|
copyToClipboard: () => copyToClipboard,
|
|
797
|
-
createApiEndpoints: () => createApiEndpoints,
|
|
798
|
-
createApiUrlBuilder: () => createApiUrlBuilder,
|
|
799
2128
|
createClientLogger: () => createClientLogger,
|
|
800
2129
|
createEmptyPaginationMeta: () => createEmptyPaginationMeta,
|
|
801
2130
|
createErrorResponse: () => createErrorResponse,
|
|
802
|
-
createEventEmitter: () => createEventEmitter,
|
|
803
|
-
createHttpClient: () => createHttpClient,
|
|
804
2131
|
createRegisterFormSchema: () => createRegisterFormSchema,
|
|
805
2132
|
createSuccessResponse: () => createSuccessResponse,
|
|
806
2133
|
createTheme: () => createTheme,
|
|
807
2134
|
createThemeFromBrand: () => createThemeFromBrand,
|
|
808
2135
|
cssVar: () => cssVar,
|
|
809
|
-
deepMerge: () =>
|
|
2136
|
+
deepMerge: () => deepMerge2,
|
|
810
2137
|
defaultDarkTheme: () => defaultDarkTheme,
|
|
811
2138
|
defaultLightTheme: () => defaultLightTheme,
|
|
2139
|
+
deleteRequest: () => deleteRequest,
|
|
812
2140
|
dummyBannerData: () => dummyBannerData,
|
|
813
2141
|
dummyFaqItems: () => dummyFaqItems,
|
|
814
2142
|
dummyFeatures: () => dummyFeatures,
|
|
@@ -817,59 +2145,66 @@ __export(client_exports, {
|
|
|
817
2145
|
dummyImage: () => dummyImage,
|
|
818
2146
|
dummyPricingPlans: () => dummyPricingPlans,
|
|
819
2147
|
dummyTestimonials: () => dummyTestimonials,
|
|
820
|
-
|
|
2148
|
+
extractData: () => extractData,
|
|
2149
|
+
extractMessage: () => extractMessage,
|
|
2150
|
+
extractNestedData: () => extractNestedData,
|
|
2151
|
+
extractPaginatedData: () => extractPaginatedData,
|
|
821
2152
|
flattenToCssVars: () => flattenToCssVars,
|
|
822
2153
|
formatDate: () => formatDate,
|
|
823
|
-
formatDateForInput: () => formatDateForInput,
|
|
824
2154
|
formatDateTime: () => formatDateTime,
|
|
825
|
-
formatDateTimeForInput: () => formatDateTimeForInput,
|
|
826
|
-
formatPackageCheckResult: () => formatPackageCheckResult2,
|
|
827
2155
|
formatRelativeTime: () => formatRelativeTime,
|
|
828
2156
|
generateCssVars: () => generateCssVars,
|
|
829
|
-
|
|
2157
|
+
generateSlug: () => generateSlug,
|
|
2158
|
+
generateSnakeSlug: () => generateSnakeSlug,
|
|
2159
|
+
generateUrlSlug: () => generateUrlSlug,
|
|
830
2160
|
getContrastColor: () => getContrastColor,
|
|
831
2161
|
getErrorMessage: () => getErrorMessage,
|
|
832
2162
|
getNextPage: () => getNextPage,
|
|
833
2163
|
getPrevPage: () => getPrevPage,
|
|
2164
|
+
getRequest: () => getRequest,
|
|
834
2165
|
getResponseData: () => getResponseData,
|
|
835
2166
|
getSystemColorScheme: () => getSystemColorScheme,
|
|
836
2167
|
hasData: () => hasData,
|
|
837
2168
|
hasMorePages: () => hasMorePages,
|
|
838
2169
|
hexToRgba: () => hexToRgba,
|
|
839
2170
|
injectCssVars: () => injectCssVars,
|
|
840
|
-
isClipboardAvailable: () => isClipboardAvailable,
|
|
841
2171
|
isErrorResponse: () => isErrorResponse,
|
|
842
|
-
isForbidden: () => isForbidden,
|
|
843
|
-
isFuture: () => isFuture,
|
|
844
|
-
isNotFound: () => isNotFound,
|
|
845
|
-
isPast: () => isPast,
|
|
846
|
-
isServerError: () => isServerError,
|
|
847
|
-
isStatusError: () => isStatusError,
|
|
848
2172
|
isSuccess: () => isSuccess,
|
|
849
2173
|
isSuccessResponse: () => isSuccessResponse,
|
|
850
|
-
|
|
851
|
-
|
|
2174
|
+
isUtilErrorResponse: () => isErrorResponse2,
|
|
2175
|
+
isUtilSuccessResponse: () => isSuccessResponse2,
|
|
852
2176
|
kebabToCamel: () => kebabToCamel,
|
|
853
2177
|
loadThemeFromUrl: () => loadThemeFromUrl,
|
|
854
2178
|
loadThemeMode: () => loadThemeMode,
|
|
2179
|
+
logger: () => logger2,
|
|
855
2180
|
loginFormSchema: () => loginFormSchema,
|
|
856
2181
|
loremIpsum: () => loremIpsum,
|
|
857
2182
|
newsletterFormSchema: () => newsletterFormSchema,
|
|
858
2183
|
packageCheck: () => packageCheck,
|
|
2184
|
+
parseAxiosErrorMessage: () => parseAxiosErrorMessage,
|
|
859
2185
|
parseError: () => parseError,
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
2186
|
+
parsePaginatedResponse: () => parsePaginatedResponse,
|
|
2187
|
+
parseResponseData: () => parseResponseData,
|
|
2188
|
+
parseResponseMessage: () => parseResponseMessage,
|
|
2189
|
+
parseResponseStatus: () => parseResponseStatus,
|
|
2190
|
+
parseResponseStatusMessage: () => parseResponseStatusMessage,
|
|
2191
|
+
patchRequest: () => patchRequest,
|
|
2192
|
+
postRequest: () => postRequest,
|
|
2193
|
+
putRequest: () => putRequest,
|
|
863
2194
|
registerFormSchema: () => registerFormSchema,
|
|
864
2195
|
removeCssVars: () => removeCssVars,
|
|
865
2196
|
resolveThemeMode: () => resolveThemeMode,
|
|
2197
|
+
safeJsonParse: () => safeJsonParse,
|
|
866
2198
|
saveThemeMode: () => saveThemeMode,
|
|
2199
|
+
simpleMetaParseResponse: () => simpleMetaParseResponse,
|
|
2200
|
+
simpleParseDualDataResponse: () => simpleParseDualDataResponse,
|
|
2201
|
+
simpleParseResponse: () => simpleParseResponse,
|
|
867
2202
|
slugify: () => slugify,
|
|
868
2203
|
slugifyUnique: () => slugifyUnique,
|
|
869
|
-
startOfDay: () => startOfDay,
|
|
870
2204
|
truncate: () => truncate,
|
|
871
2205
|
truncateWords: () => truncateWords,
|
|
872
2206
|
unslugify: () => unslugify,
|
|
2207
|
+
uploadFile: () => uploadFile,
|
|
873
2208
|
useBattery: () => useBattery_default,
|
|
874
2209
|
useClickAway: () => useClickAway_default,
|
|
875
2210
|
useContinuousRetry: () => useContinuousRetry_default,
|
|
@@ -929,112 +2264,466 @@ __export(client_exports, {
|
|
|
929
2264
|
useToggle: () => useToggle_default,
|
|
930
2265
|
useVisibilityChange: () => useVisibilityChange_default,
|
|
931
2266
|
useWindowScroll: () => useWindowScroll_default,
|
|
932
|
-
useWindowSize: () => useWindowSize_default
|
|
933
|
-
withAbortSignal: () => withAbortSignal,
|
|
934
|
-
withFormData: () => withFormData,
|
|
935
|
-
withTimeout: () => withTimeout
|
|
2267
|
+
useWindowSize: () => useWindowSize_default
|
|
936
2268
|
});
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
2269
|
+
|
|
2270
|
+
// src/client/http/logger.ts
|
|
2271
|
+
var Logger = class {
|
|
2272
|
+
constructor() {
|
|
2273
|
+
this.isDevelopment = typeof window !== "undefined" && window.location.hostname === "localhost";
|
|
2274
|
+
}
|
|
2275
|
+
/**
|
|
2276
|
+
* Log informational messages
|
|
2277
|
+
*/
|
|
2278
|
+
info(message, data, options) {
|
|
2279
|
+
if (this.isDevelopment) {
|
|
2280
|
+
const prefix = options?.context ? `[${options.context}]` : "";
|
|
2281
|
+
console.log(`${prefix} ${message}`, data ?? "");
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Log warning messages
|
|
2286
|
+
*/
|
|
2287
|
+
warn(message, data, options) {
|
|
2288
|
+
if (this.isDevelopment) {
|
|
2289
|
+
const prefix = options?.context ? `[${options.context}]` : "";
|
|
2290
|
+
console.warn(`${prefix} ${message}`, data ?? "");
|
|
952
2291
|
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Log error messages
|
|
2295
|
+
*/
|
|
2296
|
+
error(message, error, options) {
|
|
2297
|
+
const prefix = options?.context ? `[${options.context}]` : "";
|
|
2298
|
+
if (this.isDevelopment) {
|
|
2299
|
+
console.error(`${prefix} ${message}`, error, options?.metadata || "");
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Log debug messages (only in development)
|
|
2304
|
+
*/
|
|
2305
|
+
debug(message, data, options) {
|
|
2306
|
+
if (this.isDevelopment) {
|
|
2307
|
+
const prefix = options?.context ? `[${options.context}]` : "";
|
|
2308
|
+
console.debug(`${prefix} ${message}`, data || "");
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Log API errors with structured information
|
|
2313
|
+
*/
|
|
2314
|
+
apiError(endpoint, error, metadata) {
|
|
2315
|
+
this.error(`API Error: ${endpoint}`, error, {
|
|
2316
|
+
context: "API",
|
|
2317
|
+
metadata: {
|
|
2318
|
+
endpoint,
|
|
2319
|
+
...metadata
|
|
961
2320
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
var logger2 = new Logger();
|
|
2325
|
+
|
|
2326
|
+
// src/client/http/response-parser.ts
|
|
2327
|
+
var STATUS_CODES = {
|
|
2328
|
+
SUCCESS: 200,
|
|
2329
|
+
CREATED: 201,
|
|
2330
|
+
NO_CONTENT: 204,
|
|
2331
|
+
BAD_REQUEST: 400,
|
|
2332
|
+
UNAUTHORIZED: 401,
|
|
2333
|
+
FORBIDDEN: 403,
|
|
2334
|
+
NOT_FOUND: 404,
|
|
2335
|
+
CONFLICT: 409,
|
|
2336
|
+
ERROR: 500
|
|
2337
|
+
};
|
|
2338
|
+
var STATUS_MESSAGES = {
|
|
2339
|
+
SUCCESS: "success",
|
|
2340
|
+
CREATED: "created",
|
|
2341
|
+
NO_CONTENT: "no_content",
|
|
2342
|
+
BAD_REQUEST: "bad_request",
|
|
2343
|
+
UNAUTHORIZED: "unauthorized",
|
|
2344
|
+
FORBIDDEN: "forbidden",
|
|
2345
|
+
NOT_FOUND: "not_found",
|
|
2346
|
+
CONFLICT: "conflict",
|
|
2347
|
+
ERROR: "error"
|
|
2348
|
+
};
|
|
2349
|
+
var SUCCESS_CODES = [200, 201, 204];
|
|
2350
|
+
var ERROR_CODES = [400, 401, 403, 404, 409, 500];
|
|
2351
|
+
var parseResponseData = (response, fallback = null) => {
|
|
2352
|
+
try {
|
|
2353
|
+
if (!response || typeof response !== "object") {
|
|
2354
|
+
return fallback;
|
|
2355
|
+
}
|
|
2356
|
+
const resp = response;
|
|
2357
|
+
if ("data" in resp) {
|
|
2358
|
+
return resp["data"] ?? fallback;
|
|
2359
|
+
}
|
|
2360
|
+
return response;
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
logger2.error("Error parsing response data", error);
|
|
2363
|
+
return fallback;
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
var parseResponseMessage = (response, fallback = "") => {
|
|
2367
|
+
try {
|
|
2368
|
+
if (!response || typeof response !== "object") {
|
|
2369
|
+
return fallback;
|
|
2370
|
+
}
|
|
2371
|
+
const resp = response;
|
|
2372
|
+
if ("message" in resp && typeof resp["message"] === "string") {
|
|
2373
|
+
return resp["message"];
|
|
2374
|
+
}
|
|
2375
|
+
return fallback;
|
|
2376
|
+
} catch (error) {
|
|
2377
|
+
logger2.error("Error parsing response message", error);
|
|
2378
|
+
return fallback;
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
var parseResponseStatus = (response) => {
|
|
2382
|
+
try {
|
|
2383
|
+
if (!response || typeof response !== "object") {
|
|
2384
|
+
return null;
|
|
2385
|
+
}
|
|
2386
|
+
const resp = response;
|
|
2387
|
+
if ("statusCode" in resp && typeof resp["statusCode"] === "number") {
|
|
2388
|
+
return resp["statusCode"];
|
|
2389
|
+
}
|
|
2390
|
+
if ("status" in resp && typeof resp["status"] === "number") {
|
|
2391
|
+
return resp["status"];
|
|
2392
|
+
}
|
|
2393
|
+
return null;
|
|
2394
|
+
} catch (error) {
|
|
2395
|
+
logger2.error("Error parsing response status", error);
|
|
2396
|
+
return null;
|
|
2397
|
+
}
|
|
2398
|
+
};
|
|
2399
|
+
var parseResponseStatusMessage = (response, fallback = "") => {
|
|
2400
|
+
try {
|
|
2401
|
+
if (!response || typeof response !== "object") {
|
|
2402
|
+
return fallback;
|
|
2403
|
+
}
|
|
2404
|
+
const resp = response;
|
|
2405
|
+
if ("status" in resp && typeof resp["status"] === "string") {
|
|
2406
|
+
return resp["status"];
|
|
2407
|
+
}
|
|
2408
|
+
return fallback;
|
|
2409
|
+
} catch (error) {
|
|
2410
|
+
logger2.error("Error parsing response status message", error);
|
|
2411
|
+
return fallback;
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
var isSuccessResponse = (response) => {
|
|
2415
|
+
try {
|
|
2416
|
+
const statusCode2 = parseResponseStatus(response);
|
|
2417
|
+
if (statusCode2 !== null) {
|
|
2418
|
+
return SUCCESS_CODES.includes(statusCode2);
|
|
2419
|
+
}
|
|
2420
|
+
const status = parseResponseStatusMessage(response);
|
|
2421
|
+
return [STATUS_MESSAGES.SUCCESS, STATUS_MESSAGES.CREATED, STATUS_MESSAGES.NO_CONTENT].includes(
|
|
2422
|
+
status
|
|
2423
|
+
);
|
|
2424
|
+
} catch (error) {
|
|
2425
|
+
logger2.error("Error checking response success", error);
|
|
2426
|
+
return false;
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
var isErrorResponse = (response) => {
|
|
2430
|
+
try {
|
|
2431
|
+
const statusCode2 = parseResponseStatus(response);
|
|
2432
|
+
if (statusCode2 !== null) {
|
|
2433
|
+
return ERROR_CODES.includes(statusCode2);
|
|
2434
|
+
}
|
|
2435
|
+
return false;
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
logger2.error("Error checking response error", error);
|
|
2438
|
+
return false;
|
|
2439
|
+
}
|
|
2440
|
+
};
|
|
2441
|
+
var parsePaginatedResponse = (response) => {
|
|
2442
|
+
try {
|
|
2443
|
+
if (!response || typeof response !== "object") {
|
|
2444
|
+
return { items: [], total: 0, page: 1, limit: 10 };
|
|
2445
|
+
}
|
|
2446
|
+
const resp = response;
|
|
2447
|
+
let items = [];
|
|
2448
|
+
if ("data" in resp && Array.isArray(resp["data"])) {
|
|
2449
|
+
items = resp["data"];
|
|
2450
|
+
}
|
|
2451
|
+
let total = items.length;
|
|
2452
|
+
let page = 1;
|
|
2453
|
+
let limit = 10;
|
|
2454
|
+
let totalPages;
|
|
2455
|
+
if ("paginationData" in resp && resp["paginationData"] && typeof resp["paginationData"] === "object") {
|
|
2456
|
+
const paginationData = resp["paginationData"];
|
|
2457
|
+
if ("total" in paginationData && typeof paginationData["total"] === "number") {
|
|
2458
|
+
total = paginationData["total"];
|
|
2459
|
+
}
|
|
2460
|
+
if ("page" in paginationData && typeof paginationData["page"] === "number") {
|
|
2461
|
+
page = paginationData["page"];
|
|
2462
|
+
}
|
|
2463
|
+
if ("limit" in paginationData && typeof paginationData["limit"] === "number") {
|
|
2464
|
+
limit = paginationData["limit"];
|
|
2465
|
+
}
|
|
2466
|
+
if ("totalPages" in paginationData && typeof paginationData["totalPages"] === "number") {
|
|
2467
|
+
totalPages = paginationData["totalPages"];
|
|
977
2468
|
}
|
|
978
|
-
return Promise.reject(error);
|
|
979
2469
|
}
|
|
980
|
-
|
|
981
|
-
|
|
2470
|
+
let columns;
|
|
2471
|
+
if ("columns" in resp && Array.isArray(resp["columns"])) {
|
|
2472
|
+
columns = resp["columns"];
|
|
2473
|
+
}
|
|
2474
|
+
return {
|
|
2475
|
+
items,
|
|
2476
|
+
total,
|
|
2477
|
+
page,
|
|
2478
|
+
limit,
|
|
2479
|
+
...totalPages !== void 0 && { totalPages },
|
|
2480
|
+
...columns !== void 0 && { columns }
|
|
2481
|
+
};
|
|
2482
|
+
} catch (error) {
|
|
2483
|
+
logger2.error("Error parsing paginated response", error);
|
|
2484
|
+
return { items: [], total: 0, page: 1, limit: 10 };
|
|
2485
|
+
}
|
|
982
2486
|
};
|
|
983
|
-
var
|
|
984
|
-
|
|
985
|
-
|
|
2487
|
+
var extractNestedData = (response, path2, fallback = null) => {
|
|
2488
|
+
try {
|
|
2489
|
+
const keys = path2.split(".");
|
|
2490
|
+
let current = response;
|
|
2491
|
+
for (const key of keys) {
|
|
2492
|
+
if (current && typeof current === "object" && key in current) {
|
|
2493
|
+
current = current[key];
|
|
2494
|
+
} else {
|
|
2495
|
+
return fallback;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
return current;
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
logger2.error("Error extracting nested data", error);
|
|
2501
|
+
return fallback;
|
|
986
2502
|
}
|
|
987
|
-
}
|
|
988
|
-
var
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
// src/client/http/response-parser.ts
|
|
996
|
-
var parseResponse = (response) => {
|
|
997
|
-
if (response.data?.success && response.data?.data !== void 0) {
|
|
998
|
-
return response.data.data;
|
|
2503
|
+
};
|
|
2504
|
+
var safeJsonParse = (json, fallback = null) => {
|
|
2505
|
+
try {
|
|
2506
|
+
return JSON.parse(json);
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
logger2.error("Error parsing JSON", error);
|
|
2509
|
+
return fallback;
|
|
999
2510
|
}
|
|
1000
|
-
return null;
|
|
1001
2511
|
};
|
|
1002
|
-
var
|
|
1003
|
-
|
|
2512
|
+
var parseAxiosErrorMessage = (error) => {
|
|
2513
|
+
try {
|
|
2514
|
+
if (!error || typeof error !== "object") {
|
|
2515
|
+
return "An unexpected error occurred";
|
|
2516
|
+
}
|
|
2517
|
+
const err = error;
|
|
2518
|
+
if ("response" in err && err["response"] && typeof err["response"] === "object") {
|
|
2519
|
+
const response = err["response"];
|
|
2520
|
+
if ("data" in response && response["data"] && typeof response["data"] === "object") {
|
|
2521
|
+
const data = response["data"];
|
|
2522
|
+
if ("data" in data && data["data"] && typeof data["data"] === "object") {
|
|
2523
|
+
const nestedData = data["data"];
|
|
2524
|
+
if ("message" in nestedData && typeof nestedData["message"] === "string") {
|
|
2525
|
+
return nestedData["message"];
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
if ("message" in data && typeof data["message"] === "string") {
|
|
2529
|
+
return data["message"];
|
|
2530
|
+
}
|
|
2531
|
+
if ("error" in data && typeof data["error"] === "string") {
|
|
2532
|
+
return data["error"];
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
if ("message" in err && typeof err["message"] === "string") {
|
|
2537
|
+
return err["message"];
|
|
2538
|
+
}
|
|
2539
|
+
if (typeof error === "string") {
|
|
2540
|
+
return error;
|
|
2541
|
+
}
|
|
2542
|
+
return "An unexpected error occurred";
|
|
2543
|
+
} catch (parseError2) {
|
|
2544
|
+
logger2.error("Error parsing axios error message", parseError2);
|
|
2545
|
+
return "An unexpected error occurred";
|
|
2546
|
+
}
|
|
1004
2547
|
};
|
|
1005
2548
|
var parseError = (error) => {
|
|
1006
|
-
|
|
1007
|
-
|
|
2549
|
+
try {
|
|
2550
|
+
if (!error || typeof error !== "object") {
|
|
2551
|
+
return {
|
|
2552
|
+
message: "An unexpected error occurred",
|
|
2553
|
+
statusCode: null,
|
|
2554
|
+
data: null
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
const err = error;
|
|
2558
|
+
let statusCode2 = null;
|
|
2559
|
+
let data = null;
|
|
2560
|
+
let status;
|
|
2561
|
+
if ("response" in err && err["response"] && typeof err["response"] === "object") {
|
|
2562
|
+
const response = err["response"];
|
|
2563
|
+
if ("status" in response && typeof response["status"] === "number") {
|
|
2564
|
+
statusCode2 = response["status"];
|
|
2565
|
+
}
|
|
2566
|
+
if ("data" in response && response["data"] !== void 0) {
|
|
2567
|
+
data = response["data"];
|
|
2568
|
+
if (data && typeof data === "object" && "status" in data) {
|
|
2569
|
+
const dataObj = data;
|
|
2570
|
+
if (typeof dataObj["status"] === "string") {
|
|
2571
|
+
status = dataObj["status"];
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
if (statusCode2 === null && "statusCode" in err && typeof err["statusCode"] === "number") {
|
|
2577
|
+
statusCode2 = err["statusCode"];
|
|
2578
|
+
}
|
|
2579
|
+
if (data === null && "data" in err && err["data"] !== void 0) {
|
|
2580
|
+
data = err["data"];
|
|
2581
|
+
}
|
|
2582
|
+
if (!status && "status" in err && typeof err["status"] === "string") {
|
|
2583
|
+
status = err["status"];
|
|
2584
|
+
}
|
|
2585
|
+
return {
|
|
2586
|
+
message: parseAxiosErrorMessage(error),
|
|
2587
|
+
statusCode: statusCode2,
|
|
2588
|
+
data,
|
|
2589
|
+
...status !== void 0 && { status }
|
|
2590
|
+
};
|
|
2591
|
+
} catch (err) {
|
|
2592
|
+
logger2.error("Error parsing error object", err);
|
|
2593
|
+
return {
|
|
2594
|
+
message: "An unexpected error occurred",
|
|
2595
|
+
statusCode: null,
|
|
2596
|
+
data: null
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
};
|
|
2600
|
+
var simpleParseResponse = (response) => {
|
|
2601
|
+
return response?.data?.data?.data;
|
|
2602
|
+
};
|
|
2603
|
+
var simpleMetaParseResponse = (response) => {
|
|
2604
|
+
return response?.data?.data?.meta;
|
|
2605
|
+
};
|
|
2606
|
+
var simpleParseDualDataResponse = (response) => {
|
|
2607
|
+
return response?.data?.data;
|
|
2608
|
+
};
|
|
2609
|
+
|
|
2610
|
+
// src/client/http/http.ts
|
|
2611
|
+
var isDevelopment2 = typeof window !== "undefined" && window.location.hostname === "localhost";
|
|
2612
|
+
var API_BASE_URL = isDevelopment2 ? "http://localhost:4002" : "https://service-api.exyconn.com";
|
|
2613
|
+
var API_PREFIX = "/v1/api";
|
|
2614
|
+
var axiosInstance = axios.create({
|
|
2615
|
+
baseURL: API_BASE_URL,
|
|
2616
|
+
timeout: 3e4,
|
|
2617
|
+
// 30 seconds
|
|
2618
|
+
headers: {
|
|
2619
|
+
"Content-Type": "application/json"
|
|
1008
2620
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
2621
|
+
});
|
|
2622
|
+
axiosInstance.interceptors.request.use(
|
|
2623
|
+
(config) => {
|
|
2624
|
+
try {
|
|
2625
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
2626
|
+
const selectedOrg = localStorage.getItem("selectedOrganization");
|
|
2627
|
+
if (selectedOrg) {
|
|
2628
|
+
const org = JSON.parse(selectedOrg);
|
|
2629
|
+
if (org && org._id) {
|
|
2630
|
+
config.headers["x-organization-id"] = org._id;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
} catch (error) {
|
|
2635
|
+
logger2.warn("Failed to read organization from localStorage", error);
|
|
2636
|
+
}
|
|
2637
|
+
return config;
|
|
2638
|
+
},
|
|
2639
|
+
(error) => {
|
|
2640
|
+
return Promise.reject(error);
|
|
1011
2641
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
2642
|
+
);
|
|
2643
|
+
axiosInstance.interceptors.response.use(
|
|
2644
|
+
(response) => response,
|
|
2645
|
+
(error) => {
|
|
2646
|
+
const parsedError = parseError(error);
|
|
2647
|
+
logger2.error("API Error", parsedError);
|
|
2648
|
+
return Promise.reject(parsedError);
|
|
1014
2649
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
2650
|
+
);
|
|
2651
|
+
var buildHeaders = (customHeaders) => {
|
|
2652
|
+
const headers = {
|
|
2653
|
+
"Content-Type": "application/json",
|
|
2654
|
+
...customHeaders
|
|
2655
|
+
};
|
|
2656
|
+
return headers;
|
|
2657
|
+
};
|
|
2658
|
+
var buildConfig2 = (params, customHeaders) => {
|
|
2659
|
+
const config = {
|
|
2660
|
+
headers: buildHeaders(customHeaders)
|
|
2661
|
+
};
|
|
2662
|
+
if (params) {
|
|
2663
|
+
config.params = params;
|
|
1017
2664
|
}
|
|
1018
|
-
return
|
|
2665
|
+
return config;
|
|
1019
2666
|
};
|
|
1020
|
-
var
|
|
1021
|
-
|
|
2667
|
+
var getRequest = async (url, params, customHeaders) => {
|
|
2668
|
+
const config = buildConfig2(params, customHeaders);
|
|
2669
|
+
return axiosInstance.get(url, config);
|
|
2670
|
+
};
|
|
2671
|
+
var postRequest = async (url, data, customHeaders) => {
|
|
2672
|
+
const config = buildConfig2(void 0, customHeaders);
|
|
2673
|
+
return axiosInstance.post(url, data, config);
|
|
2674
|
+
};
|
|
2675
|
+
var putRequest = async (url, data, customHeaders) => {
|
|
2676
|
+
const config = buildConfig2(void 0, customHeaders);
|
|
2677
|
+
return axiosInstance.put(url, data, config);
|
|
2678
|
+
};
|
|
2679
|
+
var patchRequest = async (url, data, customHeaders) => {
|
|
2680
|
+
const config = buildConfig2(void 0, customHeaders);
|
|
2681
|
+
return axiosInstance.patch(url, data, config);
|
|
2682
|
+
};
|
|
2683
|
+
var deleteRequest = async (url, params, customHeaders) => {
|
|
2684
|
+
const config = buildConfig2(params, customHeaders);
|
|
2685
|
+
return axiosInstance.delete(url, config);
|
|
2686
|
+
};
|
|
2687
|
+
var uploadFile = async (url, file, additionalData) => {
|
|
2688
|
+
const formData = new FormData();
|
|
2689
|
+
formData.append("file", file);
|
|
2690
|
+
if (additionalData) {
|
|
2691
|
+
Object.entries(additionalData).forEach(([key, value]) => {
|
|
2692
|
+
formData.append(key, String(value));
|
|
2693
|
+
});
|
|
2694
|
+
}
|
|
2695
|
+
const config = {
|
|
2696
|
+
headers: {
|
|
2697
|
+
"Content-Type": "multipart/form-data"
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
return axiosInstance.post(url, formData, config);
|
|
2701
|
+
};
|
|
2702
|
+
var extractData = (response) => {
|
|
2703
|
+
return parseResponseData(response.data);
|
|
2704
|
+
};
|
|
2705
|
+
var extractMessage = (response) => {
|
|
2706
|
+
return parseResponseMessage(response, "");
|
|
1022
2707
|
};
|
|
1023
|
-
var
|
|
1024
|
-
return
|
|
2708
|
+
var isSuccess = (response) => {
|
|
2709
|
+
return response.status >= 200 && response.status < 300;
|
|
1025
2710
|
};
|
|
1026
|
-
var
|
|
1027
|
-
return
|
|
2711
|
+
var extractPaginatedData = (response) => {
|
|
2712
|
+
return parsePaginatedResponse(response.data);
|
|
1028
2713
|
};
|
|
1029
|
-
|
|
1030
|
-
|
|
2714
|
+
|
|
2715
|
+
// src/client/http/slug.ts
|
|
2716
|
+
var generateSlug = (text) => {
|
|
2717
|
+
if (!text) return "";
|
|
2718
|
+
return text.trim().replace(/[^\w\s]/g, "").replace(/\s+(.)/g, (_, char) => char.toUpperCase()).replace(/\s+/g, "").replace(/^(.)/, (_, char) => char.toLowerCase());
|
|
1031
2719
|
};
|
|
1032
|
-
var
|
|
1033
|
-
|
|
2720
|
+
var generateUrlSlug = (text) => {
|
|
2721
|
+
if (!text) return "";
|
|
2722
|
+
return text.trim().toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1034
2723
|
};
|
|
1035
|
-
var
|
|
1036
|
-
|
|
1037
|
-
return
|
|
2724
|
+
var generateSnakeSlug = (text) => {
|
|
2725
|
+
if (!text) return "";
|
|
2726
|
+
return text.trim().toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
1038
2727
|
};
|
|
1039
2728
|
|
|
1040
2729
|
// src/client/logger/client-logger.ts
|
|
@@ -1190,40 +2879,6 @@ var formatRelativeTime = (date) => {
|
|
|
1190
2879
|
}
|
|
1191
2880
|
return "just now";
|
|
1192
2881
|
};
|
|
1193
|
-
var formatDateForInput = (date) => {
|
|
1194
|
-
const dateObj = new Date(date);
|
|
1195
|
-
return dateObj.toISOString().split("T")[0];
|
|
1196
|
-
};
|
|
1197
|
-
var formatDateTimeForInput = (date) => {
|
|
1198
|
-
const dateObj = new Date(date);
|
|
1199
|
-
return dateObj.toISOString().slice(0, 16);
|
|
1200
|
-
};
|
|
1201
|
-
var isToday = (date) => {
|
|
1202
|
-
const dateObj = new Date(date);
|
|
1203
|
-
const today = /* @__PURE__ */ new Date();
|
|
1204
|
-
return dateObj.getDate() === today.getDate() && dateObj.getMonth() === today.getMonth() && dateObj.getFullYear() === today.getFullYear();
|
|
1205
|
-
};
|
|
1206
|
-
var isPast = (date) => {
|
|
1207
|
-
return new Date(date).getTime() < Date.now();
|
|
1208
|
-
};
|
|
1209
|
-
var isFuture = (date) => {
|
|
1210
|
-
return new Date(date).getTime() > Date.now();
|
|
1211
|
-
};
|
|
1212
|
-
var addDays = (date, days) => {
|
|
1213
|
-
const dateObj = new Date(date);
|
|
1214
|
-
dateObj.setDate(dateObj.getDate() + days);
|
|
1215
|
-
return dateObj;
|
|
1216
|
-
};
|
|
1217
|
-
var startOfDay = (date) => {
|
|
1218
|
-
const dateObj = new Date(date);
|
|
1219
|
-
dateObj.setHours(0, 0, 0, 0);
|
|
1220
|
-
return dateObj;
|
|
1221
|
-
};
|
|
1222
|
-
var endOfDay = (date) => {
|
|
1223
|
-
const dateObj = new Date(date);
|
|
1224
|
-
dateObj.setHours(23, 59, 59, 999);
|
|
1225
|
-
return dateObj;
|
|
1226
|
-
};
|
|
1227
2882
|
|
|
1228
2883
|
// src/client/utils/clipboard.ts
|
|
1229
2884
|
var copyToClipboard = async (text) => {
|
|
@@ -1248,20 +2903,6 @@ var copyToClipboard = async (text) => {
|
|
|
1248
2903
|
return false;
|
|
1249
2904
|
}
|
|
1250
2905
|
};
|
|
1251
|
-
var readFromClipboard = async () => {
|
|
1252
|
-
try {
|
|
1253
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
1254
|
-
return await navigator.clipboard.readText();
|
|
1255
|
-
}
|
|
1256
|
-
return null;
|
|
1257
|
-
} catch (error) {
|
|
1258
|
-
console.error("Failed to read from clipboard:", error);
|
|
1259
|
-
return null;
|
|
1260
|
-
}
|
|
1261
|
-
};
|
|
1262
|
-
var isClipboardAvailable = () => {
|
|
1263
|
-
return !!(navigator.clipboard && window.isSecureContext);
|
|
1264
|
-
};
|
|
1265
2906
|
|
|
1266
2907
|
// src/client/utils/slug.ts
|
|
1267
2908
|
var slugify = (text) => {
|
|
@@ -1298,165 +2939,15 @@ var kebabToCamel = (text) => {
|
|
|
1298
2939
|
return text.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
1299
2940
|
};
|
|
1300
2941
|
|
|
1301
|
-
// src/client/utils/events.ts
|
|
1302
|
-
var EventEmitter = class {
|
|
1303
|
-
constructor() {
|
|
1304
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Subscribe to an event
|
|
1308
|
-
* @returns Unsubscribe function
|
|
1309
|
-
*/
|
|
1310
|
-
on(event, handler) {
|
|
1311
|
-
if (!this.handlers.has(event)) {
|
|
1312
|
-
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
1313
|
-
}
|
|
1314
|
-
this.handlers.get(event).add(handler);
|
|
1315
|
-
return () => this.off(event, handler);
|
|
1316
|
-
}
|
|
1317
|
-
/**
|
|
1318
|
-
* Subscribe to an event once
|
|
1319
|
-
*/
|
|
1320
|
-
once(event, handler) {
|
|
1321
|
-
const wrappedHandler = (data) => {
|
|
1322
|
-
this.off(event, wrappedHandler);
|
|
1323
|
-
handler(data);
|
|
1324
|
-
};
|
|
1325
|
-
return this.on(event, wrappedHandler);
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Unsubscribe from an event
|
|
1329
|
-
*/
|
|
1330
|
-
off(event, handler) {
|
|
1331
|
-
const eventHandlers = this.handlers.get(event);
|
|
1332
|
-
if (eventHandlers) {
|
|
1333
|
-
eventHandlers.delete(handler);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Emit an event
|
|
1338
|
-
*/
|
|
1339
|
-
emit(event, data) {
|
|
1340
|
-
const eventHandlers = this.handlers.get(event);
|
|
1341
|
-
if (eventHandlers) {
|
|
1342
|
-
eventHandlers.forEach((handler) => {
|
|
1343
|
-
try {
|
|
1344
|
-
handler(data);
|
|
1345
|
-
} catch (error) {
|
|
1346
|
-
console.error(`Error in event handler for "${String(event)}":`, error);
|
|
1347
|
-
}
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Remove all handlers for an event (or all events)
|
|
1353
|
-
*/
|
|
1354
|
-
removeAllListeners(event) {
|
|
1355
|
-
if (event) {
|
|
1356
|
-
this.handlers.delete(event);
|
|
1357
|
-
} else {
|
|
1358
|
-
this.handlers.clear();
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
/**
|
|
1362
|
-
* Get count of listeners for an event
|
|
1363
|
-
*/
|
|
1364
|
-
listenerCount(event) {
|
|
1365
|
-
return this.handlers.get(event)?.size ?? 0;
|
|
1366
|
-
}
|
|
1367
|
-
};
|
|
1368
|
-
var createEventEmitter = () => {
|
|
1369
|
-
return new EventEmitter();
|
|
1370
|
-
};
|
|
1371
|
-
var appEvents = new EventEmitter();
|
|
1372
|
-
|
|
1373
|
-
// src/client/utils/api-urls.ts
|
|
1374
|
-
var ApiUrlBuilder = class {
|
|
1375
|
-
constructor(config) {
|
|
1376
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
1377
|
-
this.version = config.version || "";
|
|
1378
|
-
}
|
|
1379
|
-
/**
|
|
1380
|
-
* Build full URL from path
|
|
1381
|
-
*/
|
|
1382
|
-
build(path2) {
|
|
1383
|
-
const normalizedPath = path2.startsWith("/") ? path2 : `/${path2}`;
|
|
1384
|
-
const versionPath = this.version ? `/${this.version}` : "";
|
|
1385
|
-
return `${this.baseUrl}${versionPath}${normalizedPath}`;
|
|
1386
|
-
}
|
|
1387
|
-
/**
|
|
1388
|
-
* Build URL with query parameters
|
|
1389
|
-
*/
|
|
1390
|
-
buildWithParams(path2, params) {
|
|
1391
|
-
const url = this.build(path2);
|
|
1392
|
-
const filteredParams = Object.entries(params).filter(([, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`).join("&");
|
|
1393
|
-
return filteredParams ? `${url}?${filteredParams}` : url;
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Build URL with path parameters
|
|
1397
|
-
*/
|
|
1398
|
-
buildWithPathParams(template, params) {
|
|
1399
|
-
let path2 = template;
|
|
1400
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
1401
|
-
path2 = path2.replace(`:${key}`, String(value));
|
|
1402
|
-
path2 = path2.replace(`{${key}}`, String(value));
|
|
1403
|
-
});
|
|
1404
|
-
return this.build(path2);
|
|
1405
|
-
}
|
|
1406
|
-
/**
|
|
1407
|
-
* Get base URL
|
|
1408
|
-
*/
|
|
1409
|
-
getBaseUrl() {
|
|
1410
|
-
return this.baseUrl;
|
|
1411
|
-
}
|
|
1412
|
-
/**
|
|
1413
|
-
* Set new base URL
|
|
1414
|
-
*/
|
|
1415
|
-
setBaseUrl(baseUrl) {
|
|
1416
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
1417
|
-
}
|
|
1418
|
-
};
|
|
1419
|
-
var createApiUrlBuilder = (config) => {
|
|
1420
|
-
return new ApiUrlBuilder(config);
|
|
1421
|
-
};
|
|
1422
|
-
var createApiEndpoints = (builder) => ({
|
|
1423
|
-
// Auth endpoints
|
|
1424
|
-
auth: {
|
|
1425
|
-
login: () => builder.build("/auth/login"),
|
|
1426
|
-
register: () => builder.build("/auth/register"),
|
|
1427
|
-
logout: () => builder.build("/auth/logout"),
|
|
1428
|
-
refresh: () => builder.build("/auth/refresh"),
|
|
1429
|
-
me: () => builder.build("/auth/me"),
|
|
1430
|
-
forgotPassword: () => builder.build("/auth/forgot-password"),
|
|
1431
|
-
resetPassword: () => builder.build("/auth/reset-password")
|
|
1432
|
-
},
|
|
1433
|
-
// User endpoints
|
|
1434
|
-
users: {
|
|
1435
|
-
list: () => builder.build("/users"),
|
|
1436
|
-
get: (id) => builder.buildWithPathParams("/users/:id", { id }),
|
|
1437
|
-
create: () => builder.build("/users"),
|
|
1438
|
-
update: (id) => builder.buildWithPathParams("/users/:id", { id }),
|
|
1439
|
-
delete: (id) => builder.buildWithPathParams("/users/:id", { id })
|
|
1440
|
-
},
|
|
1441
|
-
// Generic CRUD factory
|
|
1442
|
-
crud: (resource) => ({
|
|
1443
|
-
list: () => builder.build(`/${resource}`),
|
|
1444
|
-
get: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id }),
|
|
1445
|
-
create: () => builder.build(`/${resource}`),
|
|
1446
|
-
update: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id }),
|
|
1447
|
-
delete: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id })
|
|
1448
|
-
})
|
|
1449
|
-
});
|
|
1450
|
-
|
|
1451
2942
|
// src/client/utils/response-parser.ts
|
|
1452
|
-
var
|
|
2943
|
+
var isSuccessResponse2 = (response) => {
|
|
1453
2944
|
return response.success === true;
|
|
1454
2945
|
};
|
|
1455
|
-
var
|
|
2946
|
+
var isErrorResponse2 = (response) => {
|
|
1456
2947
|
return response.success === false;
|
|
1457
2948
|
};
|
|
1458
2949
|
var getResponseData = (response, defaultValue) => {
|
|
1459
|
-
if (
|
|
2950
|
+
if (isSuccessResponse2(response) && response.data !== void 0) {
|
|
1460
2951
|
return response.data;
|
|
1461
2952
|
}
|
|
1462
2953
|
return defaultValue;
|
|
@@ -3697,18 +5188,18 @@ function useLogger(componentName, props, options = {}) {
|
|
|
3697
5188
|
const {
|
|
3698
5189
|
logProps = true,
|
|
3699
5190
|
logLifecycle = true,
|
|
3700
|
-
logger:
|
|
5191
|
+
logger: logger3 = console.log
|
|
3701
5192
|
} = options;
|
|
3702
5193
|
const previousProps = useRef(props);
|
|
3703
5194
|
const renderCount = useRef(0);
|
|
3704
5195
|
renderCount.current++;
|
|
3705
5196
|
useEffect(() => {
|
|
3706
5197
|
if (logLifecycle) {
|
|
3707
|
-
|
|
5198
|
+
logger3(`[${componentName}] Mounted`);
|
|
3708
5199
|
}
|
|
3709
5200
|
return () => {
|
|
3710
5201
|
if (logLifecycle) {
|
|
3711
|
-
|
|
5202
|
+
logger3(`[${componentName}] Unmounted (rendered ${renderCount.current} times)`);
|
|
3712
5203
|
}
|
|
3713
5204
|
};
|
|
3714
5205
|
}, []);
|
|
@@ -3740,12 +5231,12 @@ function useLogger(componentName, props, options = {}) {
|
|
|
3740
5231
|
});
|
|
3741
5232
|
}
|
|
3742
5233
|
if (hasChanges) {
|
|
3743
|
-
|
|
5234
|
+
logger3(`[${componentName}] Props changed:`, changedProps);
|
|
3744
5235
|
}
|
|
3745
5236
|
previousProps.current = props;
|
|
3746
|
-
}, [componentName, props, logProps,
|
|
5237
|
+
}, [componentName, props, logProps, logger3]);
|
|
3747
5238
|
if (process.env.NODE_ENV === "development") {
|
|
3748
|
-
|
|
5239
|
+
logger3(`[${componentName}] Render #${renderCount.current}`);
|
|
3749
5240
|
}
|
|
3750
5241
|
}
|
|
3751
5242
|
var useLogger_default = useLogger;
|
|
@@ -4943,14 +6434,14 @@ var defaultDarkTheme = {
|
|
|
4943
6434
|
};
|
|
4944
6435
|
|
|
4945
6436
|
// src/client/web/theme/theme-utils.ts
|
|
4946
|
-
function
|
|
6437
|
+
function deepMerge2(target, source) {
|
|
4947
6438
|
const output = { ...target };
|
|
4948
6439
|
for (const key in source) {
|
|
4949
6440
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
4950
6441
|
const sourceValue = source[key];
|
|
4951
6442
|
const targetValue = target[key];
|
|
4952
6443
|
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
4953
|
-
output[key] =
|
|
6444
|
+
output[key] = deepMerge2(
|
|
4954
6445
|
targetValue,
|
|
4955
6446
|
sourceValue
|
|
4956
6447
|
);
|
|
@@ -5016,7 +6507,7 @@ function createThemeFromBrand(brand, baseTheme = defaultLightTheme) {
|
|
|
5016
6507
|
}
|
|
5017
6508
|
}
|
|
5018
6509
|
};
|
|
5019
|
-
return
|
|
6510
|
+
return deepMerge2(baseTheme, brandOverrides);
|
|
5020
6511
|
}
|
|
5021
6512
|
function adjustColor(hex, percent) {
|
|
5022
6513
|
hex = hex.replace(/^#/, "");
|
|
@@ -5110,8 +6601,8 @@ async function createTheme(config = {}) {
|
|
|
5110
6601
|
if (config.themeUrl) {
|
|
5111
6602
|
const urlTheme = await loadThemeFromUrl(config.themeUrl);
|
|
5112
6603
|
if (urlTheme) {
|
|
5113
|
-
lightTheme =
|
|
5114
|
-
darkTheme =
|
|
6604
|
+
lightTheme = deepMerge2(lightTheme, urlTheme);
|
|
6605
|
+
darkTheme = deepMerge2(darkTheme, urlTheme);
|
|
5115
6606
|
}
|
|
5116
6607
|
}
|
|
5117
6608
|
if (config.brandIdentity) {
|
|
@@ -5119,10 +6610,10 @@ async function createTheme(config = {}) {
|
|
|
5119
6610
|
darkTheme = createThemeFromBrand(config.brandIdentity, darkTheme);
|
|
5120
6611
|
}
|
|
5121
6612
|
if (config.light) {
|
|
5122
|
-
lightTheme =
|
|
6613
|
+
lightTheme = deepMerge2(lightTheme, config.light);
|
|
5123
6614
|
}
|
|
5124
6615
|
if (config.dark) {
|
|
5125
|
-
darkTheme =
|
|
6616
|
+
darkTheme = deepMerge2(darkTheme, config.dark);
|
|
5126
6617
|
}
|
|
5127
6618
|
return { light: lightTheme, dark: darkTheme };
|
|
5128
6619
|
}
|
|
@@ -5196,7 +6687,7 @@ function ThemeProvider({
|
|
|
5196
6687
|
theme2 = createThemeFromBrand(brandIdentity, theme2);
|
|
5197
6688
|
}
|
|
5198
6689
|
if (lightOverrides) {
|
|
5199
|
-
theme2 =
|
|
6690
|
+
theme2 = deepMerge2(theme2, lightOverrides);
|
|
5200
6691
|
}
|
|
5201
6692
|
return theme2;
|
|
5202
6693
|
});
|
|
@@ -5207,7 +6698,7 @@ function ThemeProvider({
|
|
|
5207
6698
|
theme2 = createThemeFromBrand(brandIdentity, theme2);
|
|
5208
6699
|
}
|
|
5209
6700
|
if (darkOverrides) {
|
|
5210
|
-
theme2 =
|
|
6701
|
+
theme2 = deepMerge2(theme2, darkOverrides);
|
|
5211
6702
|
}
|
|
5212
6703
|
return theme2;
|
|
5213
6704
|
});
|
|
@@ -5226,8 +6717,8 @@ function ThemeProvider({
|
|
|
5226
6717
|
setError(null);
|
|
5227
6718
|
loadThemeFromUrl(themeUrl).then((urlTheme) => {
|
|
5228
6719
|
if (urlTheme) {
|
|
5229
|
-
setLightTheme((prev) =>
|
|
5230
|
-
setDarkTheme((prev) =>
|
|
6720
|
+
setLightTheme((prev) => deepMerge2(prev, urlTheme));
|
|
6721
|
+
setDarkTheme((prev) => deepMerge2(prev, urlTheme));
|
|
5231
6722
|
}
|
|
5232
6723
|
}).catch((err) => {
|
|
5233
6724
|
setError(err instanceof Error ? err.message : "Failed to load theme");
|
|
@@ -5272,8 +6763,8 @@ function ThemeProvider({
|
|
|
5272
6763
|
});
|
|
5273
6764
|
}, []);
|
|
5274
6765
|
const updateTheme = useCallback((updates) => {
|
|
5275
|
-
setLightTheme((prev) =>
|
|
5276
|
-
setDarkTheme((prev) =>
|
|
6766
|
+
setLightTheme((prev) => deepMerge2(prev, updates));
|
|
6767
|
+
setDarkTheme((prev) => deepMerge2(prev, updates));
|
|
5277
6768
|
}, []);
|
|
5278
6769
|
const resetTheme = useCallback(() => {
|
|
5279
6770
|
let light = defaultLightTheme;
|
|
@@ -5283,10 +6774,10 @@ function ThemeProvider({
|
|
|
5283
6774
|
dark = createThemeFromBrand(brandIdentity, dark);
|
|
5284
6775
|
}
|
|
5285
6776
|
if (lightOverrides) {
|
|
5286
|
-
light =
|
|
6777
|
+
light = deepMerge2(light, lightOverrides);
|
|
5287
6778
|
}
|
|
5288
6779
|
if (darkOverrides) {
|
|
5289
|
-
dark =
|
|
6780
|
+
dark = deepMerge2(dark, darkOverrides);
|
|
5290
6781
|
}
|
|
5291
6782
|
setLightTheme(light);
|
|
5292
6783
|
setDarkTheme(dark);
|
|
@@ -6569,7 +8060,7 @@ __export(shared_exports, {
|
|
|
6569
8060
|
VALIDATION_PATTERNS: () => VALIDATION_PATTERNS,
|
|
6570
8061
|
VALIDATION_RULES: () => VALIDATION_RULES,
|
|
6571
8062
|
VISIBILITY: () => VISIBILITY,
|
|
6572
|
-
addDays: () => addDays
|
|
8063
|
+
addDays: () => addDays,
|
|
6573
8064
|
addHours: () => addHours,
|
|
6574
8065
|
addMinutes: () => addMinutes,
|
|
6575
8066
|
addMonths: () => addMonths,
|
|
@@ -6591,7 +8082,7 @@ __export(shared_exports, {
|
|
|
6591
8082
|
eachDayOfInterval: () => eachDayOfInterval,
|
|
6592
8083
|
eachMonthOfInterval: () => eachMonthOfInterval,
|
|
6593
8084
|
eachWeekOfInterval: () => eachWeekOfInterval,
|
|
6594
|
-
endOfDay: () => endOfDay
|
|
8085
|
+
endOfDay: () => endOfDay,
|
|
6595
8086
|
endOfMonth: () => endOfMonth,
|
|
6596
8087
|
endOfWeek: () => endOfWeek,
|
|
6597
8088
|
endOfYear: () => endOfYear,
|
|
@@ -6634,18 +8125,18 @@ __export(shared_exports, {
|
|
|
6634
8125
|
isDateBetween: () => isDateBetween,
|
|
6635
8126
|
isDev: () => isDev,
|
|
6636
8127
|
isEqual: () => isEqual,
|
|
6637
|
-
isFuture: () => isFuture
|
|
6638
|
-
isPast: () => isPast
|
|
8128
|
+
isFuture: () => isFuture,
|
|
8129
|
+
isPast: () => isPast,
|
|
6639
8130
|
isProd: () => isProd,
|
|
6640
8131
|
isSameDay: () => isSameDay,
|
|
6641
8132
|
isSameMonth: () => isSameMonth,
|
|
6642
8133
|
isSameWeek: () => isSameWeek,
|
|
6643
8134
|
isSameYear: () => isSameYear,
|
|
6644
|
-
isTest: () =>
|
|
8135
|
+
isTest: () => isTest2,
|
|
6645
8136
|
isThisMonth: () => isThisMonth,
|
|
6646
8137
|
isThisWeek: () => isThisWeek,
|
|
6647
8138
|
isThisYear: () => isThisYear,
|
|
6648
|
-
isToday: () => isToday
|
|
8139
|
+
isToday: () => isToday,
|
|
6649
8140
|
isTomorrow: () => isTomorrow,
|
|
6650
8141
|
isValid: () => isValid,
|
|
6651
8142
|
isValidDate: () => isValidDate,
|
|
@@ -6665,7 +8156,7 @@ __export(shared_exports, {
|
|
|
6665
8156
|
setMinutes: () => setMinutes,
|
|
6666
8157
|
setSeconds: () => setSeconds,
|
|
6667
8158
|
setTime: () => setTime,
|
|
6668
|
-
startOfDay: () => startOfDay
|
|
8159
|
+
startOfDay: () => startOfDay,
|
|
6669
8160
|
startOfMonth: () => startOfMonth,
|
|
6670
8161
|
startOfWeek: () => startOfWeek,
|
|
6671
8162
|
startOfYear: () => startOfYear,
|
|
@@ -6720,7 +8211,7 @@ var isProd = () => {
|
|
|
6720
8211
|
var isDev = () => {
|
|
6721
8212
|
return process.env.NODE_ENV === "development";
|
|
6722
8213
|
};
|
|
6723
|
-
var
|
|
8214
|
+
var isTest2 = () => {
|
|
6724
8215
|
return process.env.NODE_ENV === "test";
|
|
6725
8216
|
};
|
|
6726
8217
|
var validateEnv = (requiredVars) => {
|
|
@@ -7159,7 +8650,7 @@ var formatRelativeTime2 = (date, baseDate) => {
|
|
|
7159
8650
|
};
|
|
7160
8651
|
var formatSmartDate = (date) => {
|
|
7161
8652
|
const parsed = parseDate(date);
|
|
7162
|
-
if (isToday
|
|
8653
|
+
if (isToday(parsed)) {
|
|
7163
8654
|
return `Today at ${format(parsed, DATE_FORMATS.TIME_SHORT)}`;
|
|
7164
8655
|
}
|
|
7165
8656
|
if (isYesterday(parsed)) {
|
|
@@ -7215,7 +8706,7 @@ var addTime = (date, amount, unit) => {
|
|
|
7215
8706
|
case "hours":
|
|
7216
8707
|
return addHours(parsed, amount);
|
|
7217
8708
|
case "days":
|
|
7218
|
-
return addDays
|
|
8709
|
+
return addDays(parsed, amount);
|
|
7219
8710
|
case "weeks":
|
|
7220
8711
|
return addWeeks(parsed, amount);
|
|
7221
8712
|
case "months":
|
|
@@ -7282,8 +8773,8 @@ var getMonthsInRange = (start, end) => {
|
|
|
7282
8773
|
var getDayBoundaries = (date) => {
|
|
7283
8774
|
const parsed = parseDate(date);
|
|
7284
8775
|
return {
|
|
7285
|
-
start: startOfDay
|
|
7286
|
-
end: endOfDay
|
|
8776
|
+
start: startOfDay(parsed),
|
|
8777
|
+
end: endOfDay(parsed)
|
|
7287
8778
|
};
|
|
7288
8779
|
};
|
|
7289
8780
|
var getWeekBoundaries = (date, weekStartsOn = 0) => {
|