3a-ecommerce-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -0
- package/dist/chunk-PEAZVBSD.mjs +597 -0
- package/dist/client-DYGi_pyp.d.mts +87 -0
- package/dist/client-DYGi_pyp.d.ts +87 -0
- package/dist/index.d.mts +496 -0
- package/dist/index.d.ts +496 -0
- package/dist/index.js +17707 -0
- package/dist/index.mjs +17043 -0
- package/dist/validation/server.d.mts +50 -0
- package/dist/validation/server.d.ts +50 -0
- package/dist/validation/server.js +518 -0
- package/dist/validation/server.mjs +168 -0
- package/package.json +69 -0
- package/src/api/address.queries.ts +96 -0
- package/src/api/category.queries.ts +85 -0
- package/src/api/coupon.queries.ts +120 -0
- package/src/api/dashboard.queries.ts +35 -0
- package/src/api/errorHandler.ts +164 -0
- package/src/api/graphqlClient.ts +113 -0
- package/src/api/index.ts +10 -0
- package/src/api/logger.client.ts +89 -0
- package/src/api/logger.ts +135 -0
- package/src/api/order.queries.ts +211 -0
- package/src/api/product.queries.ts +144 -0
- package/src/api/review.queries.ts +56 -0
- package/src/api/user.queries.ts +232 -0
- package/src/assets/3A.png +0 -0
- package/src/assets/index.ts +1 -0
- package/src/assets.d.ts +29 -0
- package/src/auth.ts +176 -0
- package/src/config/jest.backend.config.js +42 -0
- package/src/config/jest.frontend.config.js +50 -0
- package/src/config/postcss.config.js +6 -0
- package/src/config/tailwind.config.ts +70 -0
- package/src/config/tsconfig.base.json +36 -0
- package/src/config/vite.config.ts +86 -0
- package/src/config/vitest.base.config.ts +74 -0
- package/src/config/webpack.base.config.ts +126 -0
- package/src/constants/index.ts +312 -0
- package/src/cookies.ts +104 -0
- package/src/helpers.ts +400 -0
- package/src/index.ts +32 -0
- package/src/validation/client.ts +287 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/server.ts +32 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
export { y as batchValidate, h as isValidCouponCode, i as isValidEmail, p as isValidObjectId, a as isValidPassword, d as isValidPhone, f as isValidPostalCode, t as isValidSku, r as isValidUrl, j as validateCouponCode, w as validateDate, x as validateDateRange, n as validateDiscountPercentage, v as validateEmail, c as validateName, q as validateObjectId, o as validatePagination, b as validatePassword, e as validatePhone, g as validatePostalCode, k as validatePrice, l as validateQuantity, m as validateRating, u as validateSku, s as validateUrl } from '../client-DYGi_pyp.mjs';
|
|
3
|
+
import { LogLevel } from '@e-commerce/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Server-side Logger with file system support
|
|
7
|
+
* For frontend apps, use logger.client.ts instead
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface LogEntry {
|
|
11
|
+
level: LogLevel;
|
|
12
|
+
message: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
data?: any;
|
|
15
|
+
context?: string;
|
|
16
|
+
}
|
|
17
|
+
declare class Logger {
|
|
18
|
+
private static logs;
|
|
19
|
+
private static maxLogs;
|
|
20
|
+
private static enableConsole;
|
|
21
|
+
private static enableFile;
|
|
22
|
+
private static logFilePath;
|
|
23
|
+
private static logLevel;
|
|
24
|
+
static configure(options: {
|
|
25
|
+
maxLogs?: number;
|
|
26
|
+
enableConsole?: boolean;
|
|
27
|
+
enableFile?: boolean;
|
|
28
|
+
logFilePath?: string;
|
|
29
|
+
logLevel?: LogLevel | string;
|
|
30
|
+
}): void;
|
|
31
|
+
private static shouldLog;
|
|
32
|
+
private static addLog;
|
|
33
|
+
private static format;
|
|
34
|
+
static debug(message: string, data?: any, context?: string): void;
|
|
35
|
+
static info(message: string, data?: any, context?: string): void;
|
|
36
|
+
static warn(message: string, data?: any, context?: string): void;
|
|
37
|
+
static error(message: string, error?: any, context?: string): void;
|
|
38
|
+
private static log;
|
|
39
|
+
static getLogs(level?: LogLevel): LogEntry[];
|
|
40
|
+
static clearLogs(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Server-side only validation utilities
|
|
45
|
+
* These require Express and should only be used in backend services
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
declare const validate: (req: Request, res: Response, next: NextFunction) => void;
|
|
49
|
+
|
|
50
|
+
export { Logger, validate };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
export { y as batchValidate, h as isValidCouponCode, i as isValidEmail, p as isValidObjectId, a as isValidPassword, d as isValidPhone, f as isValidPostalCode, t as isValidSku, r as isValidUrl, j as validateCouponCode, w as validateDate, x as validateDateRange, n as validateDiscountPercentage, v as validateEmail, c as validateName, q as validateObjectId, o as validatePagination, b as validatePassword, e as validatePhone, g as validatePostalCode, k as validatePrice, l as validateQuantity, m as validateRating, u as validateSku, s as validateUrl } from '../client-DYGi_pyp.js';
|
|
3
|
+
import { LogLevel } from '@e-commerce/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Server-side Logger with file system support
|
|
7
|
+
* For frontend apps, use logger.client.ts instead
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface LogEntry {
|
|
11
|
+
level: LogLevel;
|
|
12
|
+
message: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
data?: any;
|
|
15
|
+
context?: string;
|
|
16
|
+
}
|
|
17
|
+
declare class Logger {
|
|
18
|
+
private static logs;
|
|
19
|
+
private static maxLogs;
|
|
20
|
+
private static enableConsole;
|
|
21
|
+
private static enableFile;
|
|
22
|
+
private static logFilePath;
|
|
23
|
+
private static logLevel;
|
|
24
|
+
static configure(options: {
|
|
25
|
+
maxLogs?: number;
|
|
26
|
+
enableConsole?: boolean;
|
|
27
|
+
enableFile?: boolean;
|
|
28
|
+
logFilePath?: string;
|
|
29
|
+
logLevel?: LogLevel | string;
|
|
30
|
+
}): void;
|
|
31
|
+
private static shouldLog;
|
|
32
|
+
private static addLog;
|
|
33
|
+
private static format;
|
|
34
|
+
static debug(message: string, data?: any, context?: string): void;
|
|
35
|
+
static info(message: string, data?: any, context?: string): void;
|
|
36
|
+
static warn(message: string, data?: any, context?: string): void;
|
|
37
|
+
static error(message: string, error?: any, context?: string): void;
|
|
38
|
+
private static log;
|
|
39
|
+
static getLogs(level?: LogLevel): LogEntry[];
|
|
40
|
+
static clearLogs(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Server-side only validation utilities
|
|
45
|
+
* These require Express and should only be used in backend services
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
declare const validate: (req: Request, res: Response, next: NextFunction) => void;
|
|
49
|
+
|
|
50
|
+
export { Logger, validate };
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/validation/server.ts
|
|
31
|
+
var server_exports = {};
|
|
32
|
+
__export(server_exports, {
|
|
33
|
+
Logger: () => Logger,
|
|
34
|
+
batchValidate: () => batchValidate,
|
|
35
|
+
isValidCouponCode: () => isValidCouponCode,
|
|
36
|
+
isValidEmail: () => isValidEmail,
|
|
37
|
+
isValidObjectId: () => isValidObjectId,
|
|
38
|
+
isValidPassword: () => isValidPassword,
|
|
39
|
+
isValidPhone: () => isValidPhone,
|
|
40
|
+
isValidPostalCode: () => isValidPostalCode,
|
|
41
|
+
isValidSku: () => isValidSku,
|
|
42
|
+
isValidUrl: () => isValidUrl,
|
|
43
|
+
validate: () => validate,
|
|
44
|
+
validateCouponCode: () => validateCouponCode,
|
|
45
|
+
validateDate: () => validateDate,
|
|
46
|
+
validateDateRange: () => validateDateRange,
|
|
47
|
+
validateDiscountPercentage: () => validateDiscountPercentage,
|
|
48
|
+
validateEmail: () => validateEmail,
|
|
49
|
+
validateName: () => validateName,
|
|
50
|
+
validateObjectId: () => validateObjectId,
|
|
51
|
+
validatePagination: () => validatePagination,
|
|
52
|
+
validatePassword: () => validatePassword,
|
|
53
|
+
validatePhone: () => validatePhone,
|
|
54
|
+
validatePostalCode: () => validatePostalCode,
|
|
55
|
+
validatePrice: () => validatePrice,
|
|
56
|
+
validateQuantity: () => validateQuantity,
|
|
57
|
+
validateRating: () => validateRating,
|
|
58
|
+
validateSku: () => validateSku,
|
|
59
|
+
validateUrl: () => validateUrl
|
|
60
|
+
});
|
|
61
|
+
module.exports = __toCommonJS(server_exports);
|
|
62
|
+
var import_express_validator = require("express-validator");
|
|
63
|
+
|
|
64
|
+
// src/constants/index.ts
|
|
65
|
+
var PORT_CONFIG = {
|
|
66
|
+
AUTH_SERVICE: 3011,
|
|
67
|
+
CATEGORY_SERVICE: 3012,
|
|
68
|
+
COUPON_SERVICE: 3013,
|
|
69
|
+
PRODUCT_SERVICE: 3014,
|
|
70
|
+
ORDER_SERVICE: 3015,
|
|
71
|
+
GRAPHQL_GATEWAY: 4e3,
|
|
72
|
+
STOREFRONT_APP: 3e3,
|
|
73
|
+
ADMIN_APP: 3001,
|
|
74
|
+
SELLER_APP: 3002,
|
|
75
|
+
SHELL_APP: 3003
|
|
76
|
+
};
|
|
77
|
+
var SERVICE_URLS = {
|
|
78
|
+
AUTH_SERVICE: `http://localhost:${PORT_CONFIG.AUTH_SERVICE}`,
|
|
79
|
+
CATEGORY_SERVICE: `http://localhost:${PORT_CONFIG.CATEGORY_SERVICE}`,
|
|
80
|
+
COUPON_SERVICE: `http://localhost:${PORT_CONFIG.COUPON_SERVICE}`,
|
|
81
|
+
PRODUCT_SERVICE: `http://localhost:${PORT_CONFIG.PRODUCT_SERVICE}`,
|
|
82
|
+
ORDER_SERVICE: `http://localhost:${PORT_CONFIG.ORDER_SERVICE}`,
|
|
83
|
+
GRAPHQL_GATEWAY: `http://localhost:${PORT_CONFIG.GRAPHQL_GATEWAY}/graphql`,
|
|
84
|
+
AUTH_API: `http://localhost:${PORT_CONFIG.AUTH_SERVICE}/api`,
|
|
85
|
+
CATEGORY_API: `http://localhost:${PORT_CONFIG.CATEGORY_SERVICE}/api`,
|
|
86
|
+
COUPON_API: `http://localhost:${PORT_CONFIG.COUPON_SERVICE}/api`,
|
|
87
|
+
PRODUCT_API: `http://localhost:${PORT_CONFIG.PRODUCT_SERVICE}/api`,
|
|
88
|
+
ORDER_API: `http://localhost:${PORT_CONFIG.ORDER_SERVICE}/api`
|
|
89
|
+
};
|
|
90
|
+
var DEFAULT_CORS_ORIGINS = [
|
|
91
|
+
"http://localhost:3000",
|
|
92
|
+
"http://localhost:3001",
|
|
93
|
+
"http://localhost:3002",
|
|
94
|
+
"http://localhost:3003"
|
|
95
|
+
];
|
|
96
|
+
var JWT_CONFIG = {
|
|
97
|
+
ACCESS_TOKEN_EXPIRY: "1h",
|
|
98
|
+
REFRESH_TOKEN_EXPIRY: "7d",
|
|
99
|
+
PASSWORD_MIN_LENGTH: 8,
|
|
100
|
+
PASSWORD_PATTERN: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
|
101
|
+
};
|
|
102
|
+
var PRODUCT_CONFIG = {
|
|
103
|
+
MIN_PRICE: 0,
|
|
104
|
+
MAX_PRICE: 999999.99,
|
|
105
|
+
MIN_STOCK: 0,
|
|
106
|
+
MAX_RATING: 5,
|
|
107
|
+
MIN_RATING: 0,
|
|
108
|
+
DEFAULT_RATING: 0,
|
|
109
|
+
MAX_IMAGE_SIZE: 5 * 1024 * 1024,
|
|
110
|
+
// 5MB
|
|
111
|
+
ALLOWED_IMAGE_TYPES: ["image/jpeg", "image/png", "image/webp"]
|
|
112
|
+
};
|
|
113
|
+
var TIMEOUT_CONFIG = {
|
|
114
|
+
API_REQUEST: 3e4,
|
|
115
|
+
// 30 seconds
|
|
116
|
+
DATABASE: 1e4,
|
|
117
|
+
// 10 seconds
|
|
118
|
+
GRAPHQL: 15e3
|
|
119
|
+
// 15 seconds
|
|
120
|
+
};
|
|
121
|
+
var REGEX_PATTERNS = {
|
|
122
|
+
EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
123
|
+
PHONE: /^[\d\-\s()]{10,}$/,
|
|
124
|
+
POSTAL_CODE: /^\d{5,6}$/,
|
|
125
|
+
COUPON_CODE: /^[A-Z0-9]{6,20}$/,
|
|
126
|
+
PRODUCT_SKU: /^[A-Z0-9\-]{3,20}$/,
|
|
127
|
+
URL: /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w.-]*)*\/?$/
|
|
128
|
+
};
|
|
129
|
+
var ENV_PRESETS = {
|
|
130
|
+
DEVELOPMENT: {
|
|
131
|
+
corsOrigins: DEFAULT_CORS_ORIGINS,
|
|
132
|
+
logLevel: "debug",
|
|
133
|
+
apiTimeout: TIMEOUT_CONFIG.API_REQUEST
|
|
134
|
+
},
|
|
135
|
+
PRODUCTION: {
|
|
136
|
+
corsOrigins: ["https://ecommerce.example.com", "https://admin.ecommerce.example.com"],
|
|
137
|
+
logLevel: "info",
|
|
138
|
+
apiTimeout: TIMEOUT_CONFIG.API_REQUEST
|
|
139
|
+
},
|
|
140
|
+
TESTING: {
|
|
141
|
+
corsOrigins: ["http://localhost:*"],
|
|
142
|
+
logLevel: "error",
|
|
143
|
+
apiTimeout: TIMEOUT_CONFIG.API_REQUEST
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/validation/client.ts
|
|
148
|
+
var isValidEmail = (email) => {
|
|
149
|
+
return REGEX_PATTERNS.EMAIL.test(email);
|
|
150
|
+
};
|
|
151
|
+
var validateEmail = (email) => {
|
|
152
|
+
if (!email || email.trim() === "") {
|
|
153
|
+
return { valid: false, error: "Email is required" };
|
|
154
|
+
}
|
|
155
|
+
if (!isValidEmail(email)) {
|
|
156
|
+
return { valid: false, error: "Invalid email format" };
|
|
157
|
+
}
|
|
158
|
+
if (email.length > 255) {
|
|
159
|
+
return { valid: false, error: "Email is too long" };
|
|
160
|
+
}
|
|
161
|
+
return { valid: true };
|
|
162
|
+
};
|
|
163
|
+
var isValidPassword = (password) => {
|
|
164
|
+
return JWT_CONFIG.PASSWORD_PATTERN.test(password);
|
|
165
|
+
};
|
|
166
|
+
var validatePassword = (password) => {
|
|
167
|
+
if (!password) {
|
|
168
|
+
return { valid: false, error: "Password is required" };
|
|
169
|
+
}
|
|
170
|
+
if (password.length < JWT_CONFIG.PASSWORD_MIN_LENGTH) {
|
|
171
|
+
return {
|
|
172
|
+
valid: false,
|
|
173
|
+
error: `Password must be at least ${JWT_CONFIG.PASSWORD_MIN_LENGTH} characters`
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (!isValidPassword(password)) {
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
error: "Password must contain uppercase, lowercase, number, and special character (@$!%*?&)"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { valid: true };
|
|
183
|
+
};
|
|
184
|
+
var validateName = (name) => {
|
|
185
|
+
if (!name || name.trim() === "") {
|
|
186
|
+
return { valid: false, error: "Name is required" };
|
|
187
|
+
}
|
|
188
|
+
if (name.length < 2) {
|
|
189
|
+
return { valid: false, error: "Name must be at least 2 characters" };
|
|
190
|
+
}
|
|
191
|
+
if (name.length > 100) {
|
|
192
|
+
return { valid: false, error: "Name is too long" };
|
|
193
|
+
}
|
|
194
|
+
if (!/^[a-zA-Z\s'-]+$/.test(name)) {
|
|
195
|
+
return {
|
|
196
|
+
valid: false,
|
|
197
|
+
error: "Name can only contain letters, spaces, hyphens, and apostrophes"
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return { valid: true };
|
|
201
|
+
};
|
|
202
|
+
var isValidPhone = (phone) => {
|
|
203
|
+
return REGEX_PATTERNS.PHONE.test(phone);
|
|
204
|
+
};
|
|
205
|
+
var validatePhone = (phone) => {
|
|
206
|
+
if (!phone) {
|
|
207
|
+
return { valid: false, error: "Phone number is required" };
|
|
208
|
+
}
|
|
209
|
+
if (!isValidPhone(phone)) {
|
|
210
|
+
return { valid: false, error: "Invalid phone number format" };
|
|
211
|
+
}
|
|
212
|
+
return { valid: true };
|
|
213
|
+
};
|
|
214
|
+
var isValidPostalCode = (postalCode) => {
|
|
215
|
+
return REGEX_PATTERNS.POSTAL_CODE.test(postalCode);
|
|
216
|
+
};
|
|
217
|
+
var validatePostalCode = (postalCode) => {
|
|
218
|
+
if (!postalCode) {
|
|
219
|
+
return { valid: false, error: "Postal code is required" };
|
|
220
|
+
}
|
|
221
|
+
if (!isValidPostalCode(postalCode)) {
|
|
222
|
+
return { valid: false, error: "Invalid postal code format" };
|
|
223
|
+
}
|
|
224
|
+
return { valid: true };
|
|
225
|
+
};
|
|
226
|
+
var isValidCouponCode = (code) => {
|
|
227
|
+
return REGEX_PATTERNS.COUPON_CODE.test(code);
|
|
228
|
+
};
|
|
229
|
+
var validateCouponCode = (code) => {
|
|
230
|
+
if (!code) {
|
|
231
|
+
return { valid: false, error: "Coupon code is required" };
|
|
232
|
+
}
|
|
233
|
+
if (!isValidCouponCode(code)) {
|
|
234
|
+
return { valid: false, error: "Invalid coupon code format (6-20 alphanumeric characters)" };
|
|
235
|
+
}
|
|
236
|
+
return { valid: true };
|
|
237
|
+
};
|
|
238
|
+
var validatePrice = (price) => {
|
|
239
|
+
const numPrice = parseFloat(price);
|
|
240
|
+
if (isNaN(numPrice)) {
|
|
241
|
+
return { valid: false, error: "Price must be a valid number" };
|
|
242
|
+
}
|
|
243
|
+
if (numPrice < 0) {
|
|
244
|
+
return { valid: false, error: "Price cannot be negative" };
|
|
245
|
+
}
|
|
246
|
+
if (numPrice > 999999.99) {
|
|
247
|
+
return { valid: false, error: "Price exceeds maximum allowed value" };
|
|
248
|
+
}
|
|
249
|
+
return { valid: true };
|
|
250
|
+
};
|
|
251
|
+
var validateQuantity = (qty) => {
|
|
252
|
+
const numQty = parseInt(qty, 10);
|
|
253
|
+
if (isNaN(numQty)) {
|
|
254
|
+
return { valid: false, error: "Quantity must be a valid number" };
|
|
255
|
+
}
|
|
256
|
+
if (numQty < 0) {
|
|
257
|
+
return { valid: false, error: "Quantity cannot be negative" };
|
|
258
|
+
}
|
|
259
|
+
if (!Number.isInteger(numQty)) {
|
|
260
|
+
return { valid: false, error: "Quantity must be a whole number" };
|
|
261
|
+
}
|
|
262
|
+
return { valid: true };
|
|
263
|
+
};
|
|
264
|
+
var validateRating = (rating) => {
|
|
265
|
+
const numRating = parseFloat(rating);
|
|
266
|
+
if (isNaN(numRating)) {
|
|
267
|
+
return { valid: false, error: "Rating must be a valid number" };
|
|
268
|
+
}
|
|
269
|
+
if (numRating < 0 || numRating > 5) {
|
|
270
|
+
return { valid: false, error: "Rating must be between 0 and 5" };
|
|
271
|
+
}
|
|
272
|
+
return { valid: true };
|
|
273
|
+
};
|
|
274
|
+
var validateDiscountPercentage = (discount) => {
|
|
275
|
+
const numDiscount = parseFloat(discount);
|
|
276
|
+
if (isNaN(numDiscount)) {
|
|
277
|
+
return { valid: false, error: "Discount must be a valid number" };
|
|
278
|
+
}
|
|
279
|
+
if (numDiscount < 0 || numDiscount > 100) {
|
|
280
|
+
return { valid: false, error: "Discount percentage must be between 0 and 100" };
|
|
281
|
+
}
|
|
282
|
+
return { valid: true };
|
|
283
|
+
};
|
|
284
|
+
var validatePagination = (page, limit) => {
|
|
285
|
+
const numPage = parseInt(page, 10) || 1;
|
|
286
|
+
const numLimit = parseInt(limit, 10) || 10;
|
|
287
|
+
if (numPage < 1) {
|
|
288
|
+
return { valid: false, error: "Page must be greater than 0" };
|
|
289
|
+
}
|
|
290
|
+
if (numLimit < 1) {
|
|
291
|
+
return { valid: false, error: "Limit must be greater than 0" };
|
|
292
|
+
}
|
|
293
|
+
if (numLimit > 100) {
|
|
294
|
+
return { valid: false, error: "Limit cannot exceed 100" };
|
|
295
|
+
}
|
|
296
|
+
return { valid: true, page: numPage, limit: numLimit };
|
|
297
|
+
};
|
|
298
|
+
var isValidObjectId = (id) => {
|
|
299
|
+
return /^[0-9a-fA-F]{24}$/.test(id);
|
|
300
|
+
};
|
|
301
|
+
var validateObjectId = (id, fieldName = "ID") => {
|
|
302
|
+
if (!id) {
|
|
303
|
+
return { valid: false, error: `${fieldName} is required` };
|
|
304
|
+
}
|
|
305
|
+
if (!isValidObjectId(id)) {
|
|
306
|
+
return { valid: false, error: `Invalid ${fieldName} format` };
|
|
307
|
+
}
|
|
308
|
+
return { valid: true };
|
|
309
|
+
};
|
|
310
|
+
var isValidUrl = (url) => {
|
|
311
|
+
return REGEX_PATTERNS.URL.test(url);
|
|
312
|
+
};
|
|
313
|
+
var validateUrl = (url) => {
|
|
314
|
+
if (!url) {
|
|
315
|
+
return { valid: false, error: "URL is required" };
|
|
316
|
+
}
|
|
317
|
+
if (!isValidUrl(url)) {
|
|
318
|
+
return { valid: false, error: "Invalid URL format" };
|
|
319
|
+
}
|
|
320
|
+
return { valid: true };
|
|
321
|
+
};
|
|
322
|
+
var isValidSku = (sku) => {
|
|
323
|
+
return REGEX_PATTERNS.PRODUCT_SKU.test(sku);
|
|
324
|
+
};
|
|
325
|
+
var validateSku = (sku) => {
|
|
326
|
+
if (!sku) {
|
|
327
|
+
return { valid: false, error: "SKU is required" };
|
|
328
|
+
}
|
|
329
|
+
if (!isValidSku(sku)) {
|
|
330
|
+
return {
|
|
331
|
+
valid: false,
|
|
332
|
+
error: "Invalid SKU format (3-20 alphanumeric characters with hyphens)"
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return { valid: true };
|
|
336
|
+
};
|
|
337
|
+
var validateDate = (date) => {
|
|
338
|
+
const dateObj = new Date(date);
|
|
339
|
+
if (isNaN(dateObj.getTime())) {
|
|
340
|
+
return { valid: false, error: "Invalid date format" };
|
|
341
|
+
}
|
|
342
|
+
return { valid: true };
|
|
343
|
+
};
|
|
344
|
+
var validateDateRange = (startDate, endDate) => {
|
|
345
|
+
const start = new Date(startDate);
|
|
346
|
+
const end = new Date(endDate);
|
|
347
|
+
if (isNaN(start.getTime())) {
|
|
348
|
+
return { valid: false, error: "Invalid start date" };
|
|
349
|
+
}
|
|
350
|
+
if (isNaN(end.getTime())) {
|
|
351
|
+
return { valid: false, error: "Invalid end date" };
|
|
352
|
+
}
|
|
353
|
+
if (start > end) {
|
|
354
|
+
return { valid: false, error: "Start date must be before end date" };
|
|
355
|
+
}
|
|
356
|
+
return { valid: true };
|
|
357
|
+
};
|
|
358
|
+
var batchValidate = (validations) => {
|
|
359
|
+
const errors = [];
|
|
360
|
+
for (const validation of validations) {
|
|
361
|
+
if (!validation.valid && validation.error) {
|
|
362
|
+
errors.push(validation.error);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
valid: errors.length === 0,
|
|
367
|
+
errors
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/api/logger.ts
|
|
372
|
+
var import_fs = __toESM(require("fs"));
|
|
373
|
+
var import_path = __toESM(require("path"));
|
|
374
|
+
|
|
375
|
+
// ../types/dist/index.mjs
|
|
376
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
377
|
+
LogLevel2["DEBUG"] = "DEBUG";
|
|
378
|
+
LogLevel2["INFO"] = "INFO";
|
|
379
|
+
LogLevel2["WARN"] = "WARN";
|
|
380
|
+
LogLevel2["ERROR"] = "ERROR";
|
|
381
|
+
return LogLevel2;
|
|
382
|
+
})(LogLevel || {});
|
|
383
|
+
|
|
384
|
+
// src/api/logger.ts
|
|
385
|
+
var LOG_LEVEL_PRIORITY = {
|
|
386
|
+
[LogLevel.DEBUG]: 0,
|
|
387
|
+
[LogLevel.INFO]: 1,
|
|
388
|
+
[LogLevel.WARN]: 2,
|
|
389
|
+
[LogLevel.ERROR]: 3
|
|
390
|
+
};
|
|
391
|
+
var Logger = class {
|
|
392
|
+
static configure(options) {
|
|
393
|
+
if (options.maxLogs !== void 0) this.maxLogs = options.maxLogs;
|
|
394
|
+
if (options.enableConsole !== void 0) this.enableConsole = options.enableConsole;
|
|
395
|
+
if (options.enableFile !== void 0) this.enableFile = options.enableFile;
|
|
396
|
+
if (options.logFilePath) this.logFilePath = options.logFilePath;
|
|
397
|
+
if (options.logLevel) {
|
|
398
|
+
const level = typeof options.logLevel === "string" ? options.logLevel.toUpperCase() : options.logLevel;
|
|
399
|
+
if (Object.values(LogLevel).includes(level)) {
|
|
400
|
+
this.logLevel = level;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
static shouldLog(level) {
|
|
405
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.logLevel];
|
|
406
|
+
}
|
|
407
|
+
static addLog(entry) {
|
|
408
|
+
this.logs.push(entry);
|
|
409
|
+
if (this.logs.length > this.maxLogs) this.logs.shift();
|
|
410
|
+
if (this.enableFile) {
|
|
411
|
+
import_fs.default.mkdirSync(import_path.default.dirname(this.logFilePath), { recursive: true });
|
|
412
|
+
import_fs.default.appendFileSync(this.logFilePath, JSON.stringify(entry) + "\n");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
static format(entry) {
|
|
416
|
+
return `[${entry.timestamp}] [${entry.level}]${entry.context ? ` [${entry.context}]` : ""}: ${entry.message}`;
|
|
417
|
+
}
|
|
418
|
+
static debug(message, data, context) {
|
|
419
|
+
this.log(LogLevel.DEBUG, message, data, context);
|
|
420
|
+
}
|
|
421
|
+
static info(message, data, context) {
|
|
422
|
+
this.log(LogLevel.INFO, message, data, context);
|
|
423
|
+
}
|
|
424
|
+
static warn(message, data, context) {
|
|
425
|
+
this.log(LogLevel.WARN, message, data, context);
|
|
426
|
+
}
|
|
427
|
+
static error(message, error, context) {
|
|
428
|
+
this.log(LogLevel.ERROR, message, error, context);
|
|
429
|
+
}
|
|
430
|
+
static log(level, message, data, context) {
|
|
431
|
+
if (!this.shouldLog(level)) return;
|
|
432
|
+
const entry = {
|
|
433
|
+
level,
|
|
434
|
+
message,
|
|
435
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
436
|
+
data,
|
|
437
|
+
context
|
|
438
|
+
};
|
|
439
|
+
this.addLog(entry);
|
|
440
|
+
if (!this.enableConsole) return;
|
|
441
|
+
const formatted = this.format(entry);
|
|
442
|
+
switch (level) {
|
|
443
|
+
case LogLevel.ERROR:
|
|
444
|
+
console.error(formatted, data ?? "");
|
|
445
|
+
break;
|
|
446
|
+
case LogLevel.WARN:
|
|
447
|
+
console.warn(formatted, data ?? "");
|
|
448
|
+
break;
|
|
449
|
+
case LogLevel.INFO:
|
|
450
|
+
console.info(formatted, data ?? "");
|
|
451
|
+
break;
|
|
452
|
+
default:
|
|
453
|
+
console.debug(formatted, data ?? "");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
static getLogs(level) {
|
|
457
|
+
return level ? this.logs.filter((l) => l.level === level) : [...this.logs];
|
|
458
|
+
}
|
|
459
|
+
static clearLogs() {
|
|
460
|
+
this.logs = [];
|
|
461
|
+
if (this.enableFile && import_fs.default.existsSync(this.logFilePath)) {
|
|
462
|
+
import_fs.default.unlinkSync(this.logFilePath);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
Logger.logs = [];
|
|
467
|
+
Logger.maxLogs = 1e3;
|
|
468
|
+
Logger.enableConsole = true;
|
|
469
|
+
Logger.enableFile = false;
|
|
470
|
+
Logger.logFilePath = import_path.default.join(process.cwd(), "logs/app.log");
|
|
471
|
+
Logger.logLevel = LogLevel.DEBUG;
|
|
472
|
+
|
|
473
|
+
// src/validation/server.ts
|
|
474
|
+
var validate = (req, res, next) => {
|
|
475
|
+
const errors = (0, import_express_validator.validationResult)(req);
|
|
476
|
+
if (!errors.isEmpty()) {
|
|
477
|
+
res.status(400).json({
|
|
478
|
+
success: false,
|
|
479
|
+
message: "Validation failed",
|
|
480
|
+
errors: errors.array().map((err) => ({
|
|
481
|
+
field: err.type === "field" ? err.path : void 0,
|
|
482
|
+
message: err.msg
|
|
483
|
+
}))
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
next();
|
|
488
|
+
};
|
|
489
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
490
|
+
0 && (module.exports = {
|
|
491
|
+
Logger,
|
|
492
|
+
batchValidate,
|
|
493
|
+
isValidCouponCode,
|
|
494
|
+
isValidEmail,
|
|
495
|
+
isValidObjectId,
|
|
496
|
+
isValidPassword,
|
|
497
|
+
isValidPhone,
|
|
498
|
+
isValidPostalCode,
|
|
499
|
+
isValidSku,
|
|
500
|
+
isValidUrl,
|
|
501
|
+
validate,
|
|
502
|
+
validateCouponCode,
|
|
503
|
+
validateDate,
|
|
504
|
+
validateDateRange,
|
|
505
|
+
validateDiscountPercentage,
|
|
506
|
+
validateEmail,
|
|
507
|
+
validateName,
|
|
508
|
+
validateObjectId,
|
|
509
|
+
validatePagination,
|
|
510
|
+
validatePassword,
|
|
511
|
+
validatePhone,
|
|
512
|
+
validatePostalCode,
|
|
513
|
+
validatePrice,
|
|
514
|
+
validateQuantity,
|
|
515
|
+
validateRating,
|
|
516
|
+
validateSku,
|
|
517
|
+
validateUrl
|
|
518
|
+
});
|