vasuzex 2.1.2 → 2.1.4
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/.ai-memory/LOGGER_STRICT_POLICY.md +201 -0
- package/.ai-memory/neastore-feature-mapping.md +1114 -0
- package/bin/create-vasuzex.js +5 -2
- package/examples/runtime-config-examples.js +309 -0
- package/framework/Config/DatabaseConfigService.js +348 -0
- package/framework/Config/DatabaseConfigServiceProvider.js +69 -0
- package/framework/Console/Commands/generate-app.js +97 -4
- package/framework/Console/Commands/generate-media-server.js +2 -1
- package/framework/Console/Commands/utils/mediaServerTemplates.js +3 -2
- package/framework/Console/Commands/utils/webStructure.js +30 -21
- package/framework/Console/config/generator.config.js +3 -3
- package/framework/Console/plopfile.js +0 -8
- package/framework/Console/templates/api/app.js.hbs +5 -4
- package/framework/Console/templates/api/server.js.hbs +8 -2
- package/framework/Database/DatabaseServiceProvider.js +1 -1
- package/framework/Database/Model.js +9 -0
- package/framework/Exceptions/index.js +2 -1
- package/framework/Foundation/BaseApp.js +19 -0
- package/framework/Foundation/BaseService.js +95 -0
- package/framework/Foundation/Container.js +18 -3
- package/framework/Foundation/Providers/index.js +0 -1
- package/framework/Foundation/ServiceProvider.js +42 -0
- package/framework/Http/asyncHandler.js +26 -0
- package/framework/Http/index.js +1 -0
- package/framework/Services/Log/LogManager.js +26 -5
- package/framework/Services/Log/LogServiceProvider.js +48 -0
- package/framework/Services/Log/index.js +1 -0
- package/framework/Services/Mail/MailServiceProvider.js +36 -0
- package/framework/Services/Mail/index.js +1 -0
- package/framework/Services/Media/MediaServiceProvider.js +2 -2
- package/framework/Services/Payment/PaymentServiceProvider.js +35 -0
- package/framework/Services/Payment/index.js +1 -0
- package/framework/Services/Security/SecurityService.js +253 -0
- package/framework/Services/Security/SecurityServiceProvider.js +33 -0
- package/framework/Services/Security/index.js +9 -0
- package/framework/Services/Storage/StorageManager.js +7 -1
- package/framework/Services/Storage/StorageServiceProvider.js +36 -0
- package/framework/Services/Storage/index.js +1 -0
- package/framework/Services/Upload/UploadManager.js +179 -0
- package/framework/Services/index.js +1 -0
- package/framework/Support/Facades/Security.js +14 -0
- package/framework/Support/Facades/index.js +1 -0
- package/framework/Support/Helpers/index.js +1 -0
- package/framework/Support/Helpers/utilities.js +348 -0
- package/framework/index.js +2 -0
- package/frontend/client/Config/ConfigLoader.js +52 -10
- package/frontend/client/Http/ApiHelpers.js +99 -0
- package/frontend/client/Http/index.js +1 -0
- package/frontend/client/index.js +1 -1
- package/frontend/client/package.json +14 -66
- package/frontend/client/package.json.backup +41 -0
- package/frontend/react-ui/components/Avatars/GradientAvatar.jsx +255 -0
- package/frontend/react-ui/components/Avatars/index.js +66 -0
- package/frontend/react-ui/components/BreadCrumb/BreadCrumb.jsx +69 -0
- package/frontend/react-ui/components/BreadCrumb/index.js +2 -0
- package/frontend/react-ui/components/DataTable/ActionDefaults.jsx +171 -0
- package/frontend/react-ui/components/DataTable/DataTable.jsx +202 -328
- package/frontend/react-ui/components/DataTable/Filters.jsx +69 -56
- package/frontend/react-ui/components/DataTable/Pagination.jsx +59 -140
- package/frontend/react-ui/components/DataTable/TableActions.jsx +11 -20
- package/frontend/react-ui/components/DataTable/TableBody.jsx +168 -168
- package/frontend/react-ui/components/DataTable/TableHeader.jsx +93 -96
- package/frontend/react-ui/components/DataTable/TableState.jsx +33 -0
- package/frontend/react-ui/components/DataTable/index.js +10 -8
- package/frontend/react-ui/components/ImageLightbox/ImageLightbox.jsx +118 -0
- package/frontend/react-ui/components/ImageLightbox/index.js +1 -0
- package/frontend/react-ui/components/OrderTimeline/OrderTimeline.jsx +269 -0
- package/frontend/react-ui/components/OrderTimeline/index.js +1 -0
- package/frontend/react-ui/components/ReadMore/ReadMore.jsx +34 -0
- package/frontend/react-ui/components/ReadMore/index.js +1 -0
- package/frontend/react-ui/components/Switch/Switch.jsx +34 -0
- package/frontend/react-ui/components/Switch/index.js +1 -0
- package/frontend/react-ui/hooks/useAppConfig.js +58 -4
- package/frontend/react-ui/hooks/useLocalStorage.js +1 -1
- package/frontend/react-ui/hooks/useValidationErrors.js +1 -1
- package/frontend/react-ui/index.js +10 -0
- package/frontend/react-ui/package.json +17 -108
- package/frontend/react-ui/providers/ApiClientProvider.jsx +1 -1
- package/frontend/react-ui/providers/AppConfigProvider.jsx +212 -20
- package/frontend/react-ui/utils/formatters.js +193 -0
- package/frontend/react-ui/utils/index.js +30 -0
- package/frontend/react-ui/utils/logger.js +62 -0
- package/frontend/react-ui/utils/storage.js +90 -0
- package/frontend/react-ui/utils/swal.js +134 -0
- package/frontend/react-ui/utils/validation.js +207 -0
- package/package.json +6 -2
- package/framework/Foundation/Providers/LogServiceProvider.js +0 -33
package/framework/Http/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { Response } from './Response.js';
|
|
|
3
3
|
export { Controller } from './Controller.js';
|
|
4
4
|
export { FormRequest } from './Requests/index.js';
|
|
5
5
|
export { ClientConfigGenerator } from './ClientConfigGenerator.js';
|
|
6
|
+
export { asyncHandler } from './asyncHandler.js';
|
|
6
7
|
export { requestResponseMiddleware } from './Middleware/RequestResponseMiddleware.js';
|
|
7
8
|
export { SecurityMiddleware, createSecurityMiddleware, applySecurityMiddleware } from './Middleware/SecurityMiddleware.js';
|
|
8
9
|
export { MediaServerMiddleware, mediaServer } from './Middleware/MediaServerMiddleware.js';
|
|
@@ -156,25 +156,46 @@ export class LogManager {
|
|
|
156
156
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Filter context based on debug mode
|
|
161
|
+
* Remove stack trace in production
|
|
162
|
+
*/
|
|
163
|
+
filterContext(context = {}) {
|
|
164
|
+
const debugValue = this.app.config('app.debug', false);
|
|
165
|
+
// Handle string 'false' or 'true' from env
|
|
166
|
+
const isDebug = debugValue === true || debugValue === 'true';
|
|
167
|
+
|
|
168
|
+
// In debug mode, return full context
|
|
169
|
+
if (isDebug) {
|
|
170
|
+
return context;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// In production, remove stack trace
|
|
174
|
+
const filtered = { ...context };
|
|
175
|
+
delete filtered.stack;
|
|
176
|
+
|
|
177
|
+
return filtered;
|
|
178
|
+
}
|
|
179
|
+
|
|
159
180
|
// Proxy PSR-3 methods to default channel
|
|
160
181
|
emergency(message, context = {}) {
|
|
161
|
-
return this.channel().emergency(message, context);
|
|
182
|
+
return this.channel().emergency(message, this.filterContext(context));
|
|
162
183
|
}
|
|
163
184
|
|
|
164
185
|
alert(message, context = {}) {
|
|
165
|
-
return this.channel().alert(message, context);
|
|
186
|
+
return this.channel().alert(message, this.filterContext(context));
|
|
166
187
|
}
|
|
167
188
|
|
|
168
189
|
critical(message, context = {}) {
|
|
169
|
-
return this.channel().critical(message, context);
|
|
190
|
+
return this.channel().critical(message, this.filterContext(context));
|
|
170
191
|
}
|
|
171
192
|
|
|
172
193
|
error(message, context = {}) {
|
|
173
|
-
return this.channel().error(message, context);
|
|
194
|
+
return this.channel().error(message, this.filterContext(context));
|
|
174
195
|
}
|
|
175
196
|
|
|
176
197
|
warning(message, context = {}) {
|
|
177
|
-
return this.channel().warning(message, context);
|
|
198
|
+
return this.channel().warning(message, this.filterContext(context));
|
|
178
199
|
}
|
|
179
200
|
|
|
180
201
|
notice(message, context = {}) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ServiceProvider } from '../../Foundation/ServiceProvider.js';
|
|
2
|
+
import { LogManager } from './LogManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Log Service Provider
|
|
6
|
+
*
|
|
7
|
+
* Registers Log service in the application container.
|
|
8
|
+
* Provides logging capabilities via multiple channels (Console, File, Syslog, Stack).
|
|
9
|
+
*/
|
|
10
|
+
export class LogServiceProvider extends ServiceProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Register the service
|
|
13
|
+
*/
|
|
14
|
+
async register() {
|
|
15
|
+
this.singleton('log', (app) => {
|
|
16
|
+
return new LogManager(app);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Create aliases
|
|
20
|
+
this.alias('logger', 'log');
|
|
21
|
+
this.alias('Logger', 'log');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bootstrap the service
|
|
26
|
+
*/
|
|
27
|
+
async boot() {
|
|
28
|
+
// Initialize default log channel only if config is loaded
|
|
29
|
+
try {
|
|
30
|
+
const defaultChannel = this.config('logging.default');
|
|
31
|
+
if (defaultChannel) {
|
|
32
|
+
const log = this.make('log');
|
|
33
|
+
|
|
34
|
+
// Pre-create default channel to verify configuration
|
|
35
|
+
try {
|
|
36
|
+
log.driver(defaultChannel);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`[LogServiceProvider] Failed to initialize log channel [${defaultChannel}]:`, error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// Config might not be loaded yet - skip initialization
|
|
43
|
+
// This is fine, log will be initialized on first use
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default LogServiceProvider;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ServiceProvider } from '../../Foundation/ServiceProvider.js';
|
|
2
|
+
import { MailManager } from './MailManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mail Service Provider
|
|
6
|
+
*
|
|
7
|
+
* Registers Mail service in the application container.
|
|
8
|
+
* Provides email sending capabilities via multiple drivers (SMTP, SendGrid, SES).
|
|
9
|
+
*/
|
|
10
|
+
export class MailServiceProvider extends ServiceProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Register the service
|
|
13
|
+
*/
|
|
14
|
+
async register() {
|
|
15
|
+
this.singleton('mail', (app) => {
|
|
16
|
+
return new MailManager(app);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Create aliases
|
|
20
|
+
this.alias('mailer', 'mail');
|
|
21
|
+
this.alias('Mail', 'mail');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bootstrap the service
|
|
26
|
+
*/
|
|
27
|
+
async boot() {
|
|
28
|
+
// Mail service is ready to use
|
|
29
|
+
if (this.config('mail.default')) {
|
|
30
|
+
const mail = this.make('mail');
|
|
31
|
+
console.log(`[MailServiceProvider] Mail service initialized with driver: ${this.config('mail.default')}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default MailServiceProvider;
|
|
@@ -11,7 +11,7 @@ export class MediaServiceProvider extends ServiceProvider {
|
|
|
11
11
|
* Register the service provider
|
|
12
12
|
*/
|
|
13
13
|
register() {
|
|
14
|
-
this.
|
|
14
|
+
this.singleton('media', (app) => {
|
|
15
15
|
return new MediaManager(app);
|
|
16
16
|
});
|
|
17
17
|
}
|
|
@@ -21,7 +21,7 @@ export class MediaServiceProvider extends ServiceProvider {
|
|
|
21
21
|
*/
|
|
22
22
|
boot() {
|
|
23
23
|
// Ensure cache directory exists
|
|
24
|
-
const media = this.
|
|
24
|
+
const media = this.make('media');
|
|
25
25
|
|
|
26
26
|
if (!existsSync(media.cacheDir)) {
|
|
27
27
|
mkdirSync(media.cacheDir, { recursive: true });
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ServiceProvider } from '../../Foundation/ServiceProvider.js';
|
|
2
|
+
import { PaymentManager } from './PaymentManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Payment Service Provider
|
|
6
|
+
*
|
|
7
|
+
* Registers Payment service in the application container.
|
|
8
|
+
* Provides payment gateway capabilities (PhonePe, Razorpay, Stripe).
|
|
9
|
+
*/
|
|
10
|
+
export class PaymentServiceProvider extends ServiceProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Register the service
|
|
13
|
+
*/
|
|
14
|
+
async register() {
|
|
15
|
+
this.singleton('payment', (app) => {
|
|
16
|
+
return new PaymentManager(app);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Create aliases
|
|
20
|
+
this.alias('Payment', 'payment');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Bootstrap the service
|
|
25
|
+
*/
|
|
26
|
+
async boot() {
|
|
27
|
+
// Payment service is ready to use
|
|
28
|
+
if (this.config('payment.default')) {
|
|
29
|
+
const payment = this.make('payment');
|
|
30
|
+
console.log(`[PaymentServiceProvider] Payment service initialized with gateway: ${this.config('payment.default')}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default PaymentServiceProvider;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { PaymentManager } from "./PaymentManager.js";
|
|
2
2
|
export { PaymentGateway } from "./PaymentGateway.js";
|
|
3
|
+
export { PaymentServiceProvider } from "./PaymentServiceProvider.js";
|
|
3
4
|
export { PhonePeGateway } from "./Gateways/PhonePeGateway.js";
|
|
4
5
|
export { RazorpayGateway } from "./Gateways/RazorpayGateway.js";
|
|
5
6
|
export { StripeGateway } from "./Gateways/StripeGateway.js";
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Service
|
|
3
|
+
* Handles JWT, OTP, hashing, and other security-related operations
|
|
4
|
+
* Laravel-inspired security utilities for Node.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import jwt from 'jsonwebtoken';
|
|
8
|
+
import bcrypt from 'bcryptjs';
|
|
9
|
+
import crypto from 'crypto';
|
|
10
|
+
|
|
11
|
+
export class SecurityService {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
this.jwtSecret = config.jwtSecret || process.env.JWT_SECRET || 'your-secret-key';
|
|
14
|
+
this.jwtExpiresIn = config.jwtExpiresIn || process.env.JWT_EXPIRES_IN || '7d';
|
|
15
|
+
this.otpLength = config.otpLength || 6;
|
|
16
|
+
this.otpExpiryMinutes = config.otpExpiryMinutes || 10;
|
|
17
|
+
this.bcryptRounds = config.bcryptRounds || 10;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generate JWT token
|
|
22
|
+
* @param {Object} payload - Data to encode in token
|
|
23
|
+
* @param {string} secret - Optional JWT secret (uses config if not provided)
|
|
24
|
+
* @param {string|number} expiresIn - Optional expiry time (uses config if not provided)
|
|
25
|
+
* @returns {string} JWT token
|
|
26
|
+
*/
|
|
27
|
+
generateToken(payload, secret = null, expiresIn = null) {
|
|
28
|
+
const jwtSecret = secret || this.jwtSecret;
|
|
29
|
+
const jwtExpiresIn = expiresIn || this.jwtExpiresIn;
|
|
30
|
+
|
|
31
|
+
return jwt.sign(payload, jwtSecret, { expiresIn: jwtExpiresIn });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verify JWT token
|
|
36
|
+
* @param {string} token - JWT token to verify
|
|
37
|
+
* @param {string} secret - Optional JWT secret (uses config if not provided)
|
|
38
|
+
* @returns {Object|null} Decoded payload or null if invalid
|
|
39
|
+
*/
|
|
40
|
+
verifyToken(token, secret = null) {
|
|
41
|
+
const jwtSecret = secret || this.jwtSecret;
|
|
42
|
+
try {
|
|
43
|
+
return jwt.verify(token, jwtSecret);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('JWT verification failed:', error.message);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Decode JWT token without verification
|
|
52
|
+
* @param {string} token - JWT token to decode
|
|
53
|
+
* @returns {Object|null} Decoded payload or null if invalid
|
|
54
|
+
*/
|
|
55
|
+
decodeToken(token) {
|
|
56
|
+
try {
|
|
57
|
+
return jwt.decode(token);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Generate numeric OTP
|
|
65
|
+
* @param {number} length - OTP length (default: 6)
|
|
66
|
+
* @returns {string} OTP code
|
|
67
|
+
*/
|
|
68
|
+
generateOtp(length = this.otpLength) {
|
|
69
|
+
const min = Math.pow(10, length - 1);
|
|
70
|
+
const max = Math.pow(10, length) - 1;
|
|
71
|
+
return Math.floor(min + Math.random() * (max - min + 1)).toString();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate alphanumeric OTP
|
|
76
|
+
* @param {number} length - OTP length
|
|
77
|
+
* @returns {string} Alphanumeric OTP
|
|
78
|
+
*/
|
|
79
|
+
generateAlphanumericOtp(length = this.otpLength) {
|
|
80
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
81
|
+
let otp = '';
|
|
82
|
+
for (let i = 0; i < length; i++) {
|
|
83
|
+
otp += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
84
|
+
}
|
|
85
|
+
return otp;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Hash password using bcrypt
|
|
90
|
+
* @param {string} password - Plain text password
|
|
91
|
+
* @returns {Promise<string>} Hashed password
|
|
92
|
+
*/
|
|
93
|
+
async hashPassword(password) {
|
|
94
|
+
return await bcrypt.hash(password, this.bcryptRounds);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Verify password against hash
|
|
99
|
+
* @param {string} password - Plain text password
|
|
100
|
+
* @param {string} hash - Hashed password
|
|
101
|
+
* @returns {Promise<boolean>} Match result
|
|
102
|
+
*/
|
|
103
|
+
async verifyPassword(password, hash) {
|
|
104
|
+
return await bcrypt.compare(password, hash);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate random string
|
|
109
|
+
* @param {number} length - String length
|
|
110
|
+
* @returns {string} Random string
|
|
111
|
+
*/
|
|
112
|
+
generateRandomString(length = 32) {
|
|
113
|
+
return crypto.randomBytes(length).toString('hex').substring(0, length);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate UUID v4
|
|
118
|
+
* @returns {string} UUID
|
|
119
|
+
*/
|
|
120
|
+
generateUuid() {
|
|
121
|
+
return crypto.randomUUID();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Hash string using SHA256
|
|
126
|
+
* @param {string} data - Data to hash
|
|
127
|
+
* @returns {string} Hash
|
|
128
|
+
*/
|
|
129
|
+
hash(data) {
|
|
130
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Hash string using MD5
|
|
135
|
+
* @param {string} data - Data to hash
|
|
136
|
+
* @returns {string} Hash
|
|
137
|
+
*/
|
|
138
|
+
md5(data) {
|
|
139
|
+
return crypto.createHash('md5').update(data).digest('hex');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create HMAC signature
|
|
144
|
+
* @param {string} data - Data to sign
|
|
145
|
+
* @param {string} secret - Secret key
|
|
146
|
+
* @param {string} algorithm - Hash algorithm (default: sha256)
|
|
147
|
+
* @returns {string} Signature
|
|
148
|
+
*/
|
|
149
|
+
createHmac(data, secret, algorithm = 'sha256') {
|
|
150
|
+
return crypto.createHmac(algorithm, secret).update(data).digest('hex');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Verify HMAC signature
|
|
155
|
+
* @param {string} data - Original data
|
|
156
|
+
* @param {string} signature - Signature to verify
|
|
157
|
+
* @param {string} secret - Secret key
|
|
158
|
+
* @param {string} algorithm - Hash algorithm
|
|
159
|
+
* @returns {boolean} Verification result
|
|
160
|
+
*/
|
|
161
|
+
verifyHmac(data, signature, secret, algorithm = 'sha256') {
|
|
162
|
+
const expectedSignature = this.createHmac(data, secret, algorithm);
|
|
163
|
+
return crypto.timingSafeEqual(
|
|
164
|
+
Buffer.from(signature),
|
|
165
|
+
Buffer.from(expectedSignature)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Encrypt data using AES-256-CBC
|
|
171
|
+
* @param {string} data - Data to encrypt
|
|
172
|
+
* @param {string} key - Encryption key (32 bytes)
|
|
173
|
+
* @returns {string} Encrypted data (iv:encrypted format)
|
|
174
|
+
*/
|
|
175
|
+
encrypt(data, key) {
|
|
176
|
+
const iv = crypto.randomBytes(16);
|
|
177
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key.padEnd(32, '0').slice(0, 32)), iv);
|
|
178
|
+
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
179
|
+
encrypted += cipher.final('hex');
|
|
180
|
+
return iv.toString('hex') + ':' + encrypted;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Decrypt data using AES-256-CBC
|
|
185
|
+
* @param {string} encrypted - Encrypted data (iv:encrypted format)
|
|
186
|
+
* @param {string} key - Encryption key (32 bytes)
|
|
187
|
+
* @returns {string} Decrypted data
|
|
188
|
+
*/
|
|
189
|
+
decrypt(encrypted, key) {
|
|
190
|
+
const parts = encrypted.split(':');
|
|
191
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
192
|
+
const encryptedData = parts[1];
|
|
193
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key.padEnd(32, '0').slice(0, 32)), iv);
|
|
194
|
+
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
|
|
195
|
+
decrypted += decipher.final('utf8');
|
|
196
|
+
return decrypted;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Mask phone number for display
|
|
201
|
+
* @param {string} phone - Phone number
|
|
202
|
+
* @returns {string} Masked phone
|
|
203
|
+
*/
|
|
204
|
+
maskPhone(phone) {
|
|
205
|
+
if (phone.length !== 10) return phone;
|
|
206
|
+
return phone.replace(/(\d{3})\d{4}(\d{3})/, '$1****$2');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Mask email for display
|
|
211
|
+
* @param {string} email - Email address
|
|
212
|
+
* @returns {string} Masked email
|
|
213
|
+
*/
|
|
214
|
+
maskEmail(email) {
|
|
215
|
+
const [username, domain] = email.split('@');
|
|
216
|
+
if (!username || !domain) return email;
|
|
217
|
+
|
|
218
|
+
const maskedUsername =
|
|
219
|
+
username.length > 2
|
|
220
|
+
? `${username[0]}${'*'.repeat(username.length - 2)}${username[username.length - 1]}`
|
|
221
|
+
: username;
|
|
222
|
+
|
|
223
|
+
return `${maskedUsername}@${domain}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Generate OTP with expiry timestamp
|
|
228
|
+
* @param {number} length - OTP length
|
|
229
|
+
* @returns {Object} {otp, expiresAt}
|
|
230
|
+
*/
|
|
231
|
+
generateOtpWithExpiry(length = this.otpLength) {
|
|
232
|
+
const otp = this.generateOtp(length);
|
|
233
|
+
const expiresAt = new Date(Date.now() + this.otpExpiryMinutes * 60 * 1000);
|
|
234
|
+
return { otp, expiresAt };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Verify OTP with expiry check
|
|
239
|
+
* @param {string} inputOtp - User input OTP
|
|
240
|
+
* @param {string} storedOtp - Stored OTP
|
|
241
|
+
* @param {Date} expiresAt - Expiry timestamp
|
|
242
|
+
* @returns {Object} {valid, expired}
|
|
243
|
+
*/
|
|
244
|
+
verifyOtp(inputOtp, storedOtp, expiresAt) {
|
|
245
|
+
const now = new Date();
|
|
246
|
+
const expired = now > new Date(expiresAt);
|
|
247
|
+
const valid = inputOtp === storedOtp;
|
|
248
|
+
|
|
249
|
+
return { valid: valid && !expired, expired };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export default SecurityService;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Service Provider
|
|
3
|
+
* Registers security service in the application container
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ServiceProvider } from '../../Foundation/ServiceProvider.js';
|
|
7
|
+
import { SecurityService } from './SecurityService.js';
|
|
8
|
+
|
|
9
|
+
export class SecurityServiceProvider extends ServiceProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Register security service
|
|
12
|
+
*/
|
|
13
|
+
async register() {
|
|
14
|
+
this.app.singleton('security', () => {
|
|
15
|
+
return new SecurityService({
|
|
16
|
+
jwtSecret: this.app.config('app.key') || this.app.config('security.jwt.secret'),
|
|
17
|
+
jwtExpiresIn: this.app.config('security.jwt.expiresIn') || '7d',
|
|
18
|
+
otpLength: this.app.config('security.otp.length') || 6,
|
|
19
|
+
otpExpiryMinutes: this.app.config('security.otp.expiryMinutes') || 10,
|
|
20
|
+
bcryptRounds: this.app.config('security.bcrypt.rounds') || 10,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Boot security service
|
|
27
|
+
*/
|
|
28
|
+
async boot() {
|
|
29
|
+
// Security service is ready
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default SecurityServiceProvider;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Service Module
|
|
3
|
+
* JWT, OTP, Hashing, and Security utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { SecurityService } from './SecurityService.js';
|
|
7
|
+
export { SecurityServiceProvider } from './SecurityServiceProvider.js';
|
|
8
|
+
import { SecurityService } from './SecurityService.js';
|
|
9
|
+
export default SecurityService;
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { LocalStorageProvider } from './Providers/LocalStorageProvider.js';
|
|
26
26
|
import { S3StorageProvider } from './Providers/S3StorageProvider.js';
|
|
27
|
+
import path from 'path';
|
|
27
28
|
|
|
28
29
|
export class StorageManager {
|
|
29
30
|
/**
|
|
@@ -93,7 +94,12 @@ export class StorageManager {
|
|
|
93
94
|
* @private
|
|
94
95
|
*/
|
|
95
96
|
createLocalDriver(config) {
|
|
96
|
-
|
|
97
|
+
// Resolve root path relative to app's rootDir
|
|
98
|
+
const resolvedConfig = { ...config };
|
|
99
|
+
if (config.root && !path.isAbsolute(config.root)) {
|
|
100
|
+
resolvedConfig.root = path.resolve(this.app.rootDir, config.root);
|
|
101
|
+
}
|
|
102
|
+
return new LocalStorageProvider(resolvedConfig);
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
/**
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ServiceProvider } from '../../Foundation/ServiceProvider.js';
|
|
2
|
+
import { StorageManager } from './StorageManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Storage Service Provider
|
|
6
|
+
*
|
|
7
|
+
* Registers Storage service in the application container.
|
|
8
|
+
* Provides file storage capabilities via multiple drivers (Local, S3, MinIO).
|
|
9
|
+
*/
|
|
10
|
+
export class StorageServiceProvider extends ServiceProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Register the service
|
|
13
|
+
*/
|
|
14
|
+
async register() {
|
|
15
|
+
this.singleton('storage', (app) => {
|
|
16
|
+
return new StorageManager(app);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Create aliases
|
|
20
|
+
this.alias('filesystem', 'storage');
|
|
21
|
+
this.alias('Storage', 'storage');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bootstrap the service
|
|
26
|
+
*/
|
|
27
|
+
async boot() {
|
|
28
|
+
// Storage service is ready to use
|
|
29
|
+
if (this.config('filesystems.default')) {
|
|
30
|
+
const storage = this.make('storage');
|
|
31
|
+
console.log(`[StorageServiceProvider] Storage service initialized with driver: ${this.config('filesystems.default')}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default StorageServiceProvider;
|