@ecopex/ecopex-framework 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/index.js +5 -0
  2. package/libraries/fastify.js +120 -0
  3. package/libraries/knex.js +1 -1
  4. package/package.json +6 -2
  5. package/stores/base.js +1 -1
  6. package/utils/jsonRouteLoader.js +3 -3
  7. package/utils/middleware.js +1 -1
  8. package/utils/routeLoader.js +1 -1
  9. package/.env +0 -73
  10. package/database/migrations/20240000135243_timezones.js +0 -22
  11. package/database/migrations/20240000135244_countries.js +0 -23
  12. package/database/migrations/20240000135244_create_admins_table.js +0 -66
  13. package/database/migrations/20240000135244_currencies.js +0 -21
  14. package/database/migrations/20240000135244_languages.js +0 -21
  15. package/database/migrations/20240000135244_taxes.js +0 -10
  16. package/database/migrations/20240000135245_sites.js +0 -37
  17. package/database/migrations/20240000135246_payment_methods.js +0 -33
  18. package/database/migrations/20251016113547_devices.js +0 -37
  19. package/database/migrations/20251019192600_users.js +0 -62
  20. package/database/migrations/20251019213551_language_lines.js +0 -35
  21. package/database/migrations/20251222214131_category_groups.js +0 -18
  22. package/database/migrations/20251222214619_categories.js +0 -27
  23. package/database/migrations/20251222214848_brands.js +0 -23
  24. package/database/migrations/20251222214946_products.js +0 -30
  25. package/database/migrations/20251222215428_product_images.js +0 -18
  26. package/database/migrations/20251222215553_options.js +0 -30
  27. package/database/migrations/20251222215806_variants.js +0 -23
  28. package/database/migrations/20251222215940_attributes.js +0 -25
  29. package/database/migrations/20251222220135_discounts.js +0 -15
  30. package/database/migrations/20251222220253_reviews.js +0 -22
  31. package/database/migrations/20251222220341_favorites.js +0 -10
  32. package/database/migrations/20251222220422_search_logs.js +0 -17
  33. package/database/migrations/20251222220636_orders.js +0 -16
  34. package/database/migrations/20251222220806_order_items.js +0 -19
  35. package/database/migrations/20251222221317_order_statuses.js +0 -10
  36. package/database/migrations/20251222221446_order_payments.js +0 -13
  37. package/database/migrations/20251222221654_order_addresses.js +0 -23
  38. package/database/migrations/20251222221807_order_status_logs.js +0 -13
  39. package/database/seeds/admins.js +0 -37
  40. package/database/seeds/countries.js +0 -203
  41. package/database/seeds/currencies.js +0 -165
  42. package/database/seeds/languages.js +0 -113
  43. package/database/seeds/timezones.js +0 -149
  44. package/ecosystem.config.js +0 -26
  45. package/libraries/stores.js +0 -22
  46. package/routes/admin/auto/admins.json +0 -63
  47. package/routes/admin/auto/devices.json +0 -37
  48. package/routes/admin/auto/migrations.json +0 -21
  49. package/routes/admin/auto/users.json +0 -61
  50. package/routes/admin/middlewares/index.js +0 -87
  51. package/routes/admin/spec/auth.js +0 -626
  52. package/routes/admin/spec/users.js +0 -3
  53. package/routes/auto/handler.js +0 -635
  54. package/routes/common/auto/countries.json +0 -28
  55. package/routes/common/auto/currencies.json +0 -26
  56. package/routes/common/auto/languages.json +0 -26
  57. package/routes/common/auto/taxes.json +0 -46
  58. package/routes/common/auto/timezones.json +0 -29
  59. package/workers/admin.js +0 -124
  60. package/workers/api.js +0 -106
@@ -1,626 +0,0 @@
1
- const JWT = require('@root/libraries/jwt');
2
- const BCRYPT = require('@root/libraries/bcrypt');
3
- const { getUtcFormated } = require('@root/libraries/date');
4
- const fs = require('fs');
5
- const QRCode = require('qrcode')
6
- const { generate_2fa_secret, verify_2fa_token } = require('@root/libraries/2fa');
7
- const DeviceDetector = require('node-device-detector');
8
- const detector = new DeviceDetector({
9
- clientIndexes: true,
10
- deviceIndexes: true,
11
- deviceAliasCode: false
12
- });
13
-
14
- const db = require('@root/libraries/knex');
15
- const jwt = new JWT();
16
- const bcrypt = new BCRYPT();
17
-
18
- const userdata_schema = {
19
- id: { type: 'integer' },
20
- role: { type: 'string', enum: ['admin', 'manager'] },
21
- firstname: { type: 'string', default: null },
22
- lastname: { type: 'string', default: null },
23
- username: { type: 'string' },
24
- email: { type: 'string', default: null },
25
- two_factor_enabled: { type: 'boolean', default: false },
26
- phone_number: { type: 'string', default: null },
27
- phone_verified: { type: 'boolean', default: false },
28
- email_verified: { type: 'boolean', default: false },
29
- is_active: { type: 'boolean', default: true },
30
- address: { type: 'string', default: null },
31
- city: { type: 'string', default: null },
32
- state: { type: 'string', default: null },
33
- zip: { type: 'string', default: null },
34
- country_id: { type: 'integer', default: null },
35
- country_name: { type: 'string', default: null },
36
- last_login_ip: { type: 'string', default: null },
37
- last_login_at: { type: 'string', format: 'date-time', default: null },
38
- login_attempts: { type: 'integer', default: 0 },
39
- locked_until: { type: 'string', format: 'date-time', default: null },
40
- created_at: { type: 'string', format: 'date-time', default: null },
41
- updated_at: { type: 'string', format: 'date-time', default: null }
42
- }
43
-
44
- const login = {
45
- method: 'POST',
46
- path: 'login',
47
- schema: {
48
- description: 'Login to the system',
49
- summary: 'Login to the system',
50
- body: {
51
- type: 'object',
52
- required: ['username', 'password', 'type'],
53
- properties: {
54
- username: { type: 'string', default: 'admin' },
55
- password: { type: 'string', default: 'password' },
56
- type: { type: 'string', enum: ['admin', 'manager'], default: 'admin' }
57
- },
58
- additionalProperties: false
59
- },
60
- headers: {
61
- type: 'object',
62
- properties: {
63
- locale: { type: 'string', default: 'en' }
64
- }
65
- },
66
- success_response: {
67
- type: 'object',
68
- properties: {
69
- status: { type: 'boolean', default: true },
70
- data: {
71
- type: 'object',
72
- properties: {
73
- token: { type: 'string', format: 'jwt', description: 'JWT token', example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZGV2aWNlIjoidW5rbm93biIsInR5cGUiOiJhZG1pbiIsImlhdCI6MTc2NzA5NjM5NCwiZXhwIjoxNzY3MTgyNzk0fQ.6DTKLdJ7tEE3ttqY1eN9lCcnhCTK8TdOPhChlAtlJjE" }
74
- },
75
- additionalProperties: false
76
- }
77
- }
78
- }
79
- },
80
- handler: async (request, reply) => {
81
-
82
- const { username, password, type } = request.body;
83
- const device = request.headers['device'] || 'unknown';
84
-
85
- let user = null;
86
-
87
- if(type === 'admin') {
88
- user = await db('admins').where('username', username).first();
89
- if(!user) {
90
- return reply.status(401).send({
91
- status: false,
92
- message: 'Invalid credentials',
93
- code: 401
94
- });
95
- }
96
- } else if(type === 'manager') {
97
- user = await db('managers').where('username', username).first();
98
- if(!user) {
99
- return reply.status(401).send({
100
- status: false,
101
- message: 'Invalid credentials',
102
- code: 401
103
- });
104
- }
105
- }
106
-
107
- if(user.locked_until && user.locked_until > new Date()) {
108
- return reply.status(401).send({
109
- status: false,
110
- message: 'Account is locked',
111
- code: 401
112
- });
113
- }
114
-
115
- if(user.login_attempts >= 5) {
116
- return reply.status(401).send({
117
- status: false,
118
- message: 'Too many login attempts',
119
- code: 401
120
- });
121
- }
122
-
123
- const isPasswordValid = await bcrypt.compare(password, user.password);
124
- if(!isPasswordValid) {
125
- await db('admins').where('admin_id', user.admin_id).update({
126
- login_attempts: user.login_attempts + 1
127
- });
128
- return reply.status(401).send({
129
- status: false,
130
- message: 'Invalid credentials',
131
- code: 401
132
- });
133
- }
134
-
135
- if(!device) {
136
- return reply.status(401).send({
137
- status: false,
138
- message: 'Device not found',
139
- code: 401
140
- });
141
- }
142
-
143
- const personal_id = user.admin_id || user.manager_id;
144
-
145
- const token = jwt.sign({
146
- id: personal_id,
147
- device: device,
148
- type: type
149
- });
150
-
151
- const device_info = detector.detect(request.headers['user-agent']);
152
- const client_ip = request.headers['x-real-ip'] || request.ip
153
-
154
- const device_informations = {
155
- os: device_info.os?.name || 'unknown',
156
- os_version: device_info.os?.version || 'unknown',
157
- client: device_info.client?.name || 'unknown',
158
- client_version: device_info.client?.version || 'unknown',
159
- device: device_info.device?.brand || 'unknown'
160
- }
161
-
162
- await db('devices').insert({
163
- personal_id: personal_id,
164
- personal_type: type,
165
- unique_id: device || '--',
166
- ip_address: client_ip,
167
- two_factor_approved: false,
168
- last_login_at: getUtcFormated(),
169
- last_login_ip: client_ip,
170
- token: token,
171
- device_information: JSON.stringify(device_informations)
172
- }).onConflict().merge(['last_login_at', 'last_login_ip', 'token', 'device_information']);
173
-
174
- // if(!user.two_factor_enabled) {
175
- // return reply.status(303).send({
176
- // status: false,
177
- // message: 'Two-factor authentication is not enabled',
178
- // details: {
179
-
180
- // two_factor_enabled: user.two_factor_enabled
181
- // },
182
- // code: 303
183
- // });
184
- // }
185
-
186
- if(user.two_factor_enabled) {
187
- let check_device = await db('devices').where({ personal_id: personal_id, personal_type: type, unique_id: device }).first()
188
-
189
- if(!check_device.two_factor_approved) {
190
- return reply.status(303).send({
191
- status: false,
192
- message: 'Device is not approved',
193
- details: {
194
- token: token,
195
- two_factor_approved: check_device.two_factor_approved
196
- },
197
- code: 303
198
- });
199
- }
200
- }
201
-
202
- await db('admins').where('admin_id', user.admin_id).update({
203
- login_attempts: 0
204
- });
205
-
206
- reply.send({
207
- status: true,
208
- data: {
209
- token: token
210
- }
211
- })
212
- }
213
- }
214
-
215
- const login_2fa = {
216
- method: 'POST',
217
- path: 'login-2fa',
218
- schema: {
219
- description: 'Login with 2FA',
220
- summary: 'After successful login, you will be redirected to the 2FA page to enter the code if 2FA is enabled',
221
- body: {
222
- type: 'object',
223
- required: ['code'],
224
- properties: {
225
- code: { type: 'string' }
226
- }
227
- },
228
- success_response: {
229
- type: 'object',
230
- properties: {
231
- status: { type: 'boolean', default: true },
232
- data: {
233
- type: 'object',
234
- properties: userdata_schema,
235
- additionalProperties: false
236
- }
237
- }
238
- }
239
- },
240
- security: 'auth',
241
- handler: async (request, reply) => {
242
- const user = request.user;
243
- const { code } = request.body;
244
- const { device } = request.headers;
245
-
246
- if(!user.two_factor_enabled) {
247
- return reply.status(303).send({
248
- status: false,
249
- message: 'Two-factor authentication is not enabled',
250
- code: 303
251
- });
252
- }
253
-
254
- const isValid = verify_2fa_token(user.two_factor_secret, code);
255
-
256
- if(!isValid) {
257
- return reply.status(400).send({
258
- status: false,
259
- message: 'Invalid code',
260
- code: 400
261
- });
262
- }
263
-
264
- await db('devices').where({ personal_id: user.id, personal_type: user.type, unique_id: device }).update({
265
- two_factor_approved: true,
266
- last_login_at: getUtcFormated(),
267
- last_login_ip: request.headers['x-real-ip'] || request.ip
268
- })
269
-
270
- await db(user.type == 'admin' ? 'admins' : 'managers').where({ admin_id: user.id }).update({
271
- login_attempts: 0
272
- })
273
-
274
- user.last_login_at = getUtcFormated();
275
- user.last_login_ip = request.headers['x-real-ip'] || request.ip;
276
-
277
- reply.send({
278
- status: true,
279
- data: user
280
- });
281
- }
282
- }
283
-
284
- const userdata = {
285
- method: 'GET',
286
- path: 'userdata',
287
- schema: {
288
- description: 'Get user data',
289
- summary: 'Get user data',
290
- headers: {
291
- type: 'object',
292
- properties: {
293
- locale: { type: 'string', default: 'en' }
294
- }
295
- },
296
- success_response: {
297
- type: 'object',
298
- properties: {
299
- status: { type: 'boolean', default: true },
300
- data: {
301
- type: 'object',
302
- properties: userdata_schema,
303
- additionalProperties: false
304
- }
305
- }
306
- }
307
- },
308
- security: 'auth',
309
- handler: async (request, reply) => {
310
- const user = request.user;
311
- reply.send({
312
- status: true,
313
- data: user
314
- })
315
- }
316
- }
317
-
318
- const update_userdata = {
319
- method: 'PUT',
320
- path: 'update-userdata',
321
- schema: {
322
- description: 'Update user data',
323
- summary: 'Update user data',
324
- body: {
325
- type: 'object',
326
- properties: {
327
- firstname: { type: 'string' },
328
- lastname: { type: 'string' },
329
- phone_number: { type: 'string' },
330
- country_id: { type: 'integer' },
331
- address: { type: 'string' },
332
- city: { type: 'string' },
333
- state: { type: 'string' },
334
- zip: { type: 'string' }
335
- }
336
- },
337
- success_response: {
338
- type: 'object',
339
- properties: {
340
- status: { type: 'boolean', default: true },
341
- data: {
342
- type: 'object',
343
- properties: userdata_schema,
344
- additionalProperties: false
345
- }
346
- }
347
- }
348
- },
349
- security: 'auth',
350
- handler: async (request, reply) => {
351
-
352
- const user = request.user;
353
- const { firstname, lastname, phone_number, country_id, address, city, state, zip } = request.body;
354
-
355
- const country = await db('countries').where('country_id', country_id).first();
356
- if(!country) {
357
- return reply.status(400).send({
358
- status: false,
359
- message: 'Country not found',
360
- code: 400
361
- });
362
- }
363
-
364
- const update_data = {}
365
-
366
- if(firstname) {
367
- update_data.firstname = firstname;
368
- }
369
- if(lastname) {
370
- update_data.lastname = lastname;
371
- }
372
- if(phone_number) {
373
- update_data.phone_number = phone_number;
374
- }
375
- if(country_id) {
376
- update_data.country_id = country_id;
377
- }
378
- if(address) {
379
- update_data.address = address;
380
- }
381
- if(city) {
382
- update_data.city = city;
383
- }
384
- if(state) {
385
- update_data.state = state;
386
- }
387
- if(zip) {
388
- update_data.zip = zip;
389
- }
390
-
391
- if(Object.keys(update_data).length > 0) {
392
- await db('admins').where('admin_id', user.id).update(update_data);
393
- }
394
-
395
- const updated_user = await db('admins').where('admin_id', user.id).first();
396
-
397
- reply.send({
398
- status: true,
399
- data: updated_user
400
- })
401
- }
402
- }
403
-
404
- const change_password = {
405
- method: 'PUT',
406
- path: 'change-password',
407
- schema: {
408
- description: 'Change password',
409
- summary: 'Change password',
410
- body: {
411
- type: 'object',
412
- required: ['old_password', 'new_password', 'new_password_again'],
413
- properties: {
414
- old_password: { type: 'string' },
415
- new_password: { type: 'string' },
416
- new_password_again: { type: 'string' }
417
- }
418
- }
419
- },
420
- security: 'auth',
421
- handler: async (request, reply) => {
422
-
423
- const user = request.user;
424
- const { old_password, new_password, new_password_again } = request.body;
425
-
426
- const isPasswordValid = await bcrypt.compare(old_password, user.password);
427
- if(!isPasswordValid) {
428
- return reply.status(400).send({
429
- status: false,
430
- message: request.t('messages.invalid_old_password'),
431
- code: 400
432
- });
433
- }
434
-
435
- if(new_password !== new_password_again) {
436
- return reply.status(400).send({
437
- status: false,
438
- message: request.t('messages.new_password_not_match'),
439
- code: 400
440
- });
441
- }
442
-
443
- const hashedPassword = await bcrypt.hash(new_password, 10);
444
- await db(user.type == 'admin' ? 'admins' : 'managers').where({ admin_id: user.id }).update({
445
- password: hashedPassword
446
- });
447
-
448
- reply.send({
449
- status: true,
450
- message: request.t('messages.password_changed_successfully')
451
- })
452
- }
453
- }
454
-
455
- const activate_2fa = {
456
- method: 'GET',
457
- path: 'activate-2fa',
458
- schema: {
459
- description: 'Create QR Code for 2FA',
460
- summary: 'Create QR Code for 2FA',
461
- success_response: {
462
- type: 'object',
463
- properties: {
464
- status: { type: 'boolean', default: true },
465
- data: { type: 'object', properties: { qr: { type: 'string' }, secret: { type: 'string' }, uri: { type: 'string' } } }
466
- }
467
- }
468
- },
469
- security: 'auth',
470
- handler: async (request, reply) => {
471
- const user = request.user;
472
-
473
- if(user.two_factor_enabled) {
474
- return reply.status(303).send({
475
- status: false,
476
- message: 'Two-factor authentication is already enabled',
477
- code: 303
478
- });
479
- }
480
-
481
- const generate_data = generate_2fa_secret(user.username, request.headers.origin)
482
-
483
- await db(user.type == 'admin' ? 'admins' : 'managers').where({ admin_id: user.id }).update({
484
- two_factor_secret: generate_data.secret
485
- })
486
-
487
- generate_data.qr = await QRCode.toDataURL(generate_data.uri);
488
-
489
- reply.send({
490
- status: true,
491
- data: generate_data
492
- })
493
- }
494
- }
495
-
496
- const activation_2fa = {
497
- method: 'POST',
498
- path: 'activation-2fa',
499
- schema: {
500
- description: 'Activation 2FA',
501
- summary: 'Activation 2FA',
502
- body: {
503
- type: 'object',
504
- required: ['code'],
505
- properties: {
506
- code: { type: 'string' }
507
- }
508
- }
509
- },
510
- security: 'auth',
511
- handler: async (request, reply) => {
512
- const user = request.user;
513
- const { code } = request.body;
514
- const { device } = request.headers;
515
-
516
- if(user.two_factor_enabled) {
517
- return reply.status(303).send({
518
- status: false,
519
- message: 'Two-factor authentication is not enabled',
520
- code: 303
521
- });
522
- }
523
-
524
- const isValid = verify_2fa_token(user.two_factor_secret, code);
525
-
526
- if(!isValid) {
527
- return reply.status(400).send({
528
- status: false,
529
- message: 'Invalid code',
530
- code: 400
531
- });
532
- }
533
-
534
- await db(user.type == 'admin' ? 'admins' : 'managers').where({ admin_id: user.id }).update({
535
- two_factor_enabled: true
536
- })
537
-
538
- await db('devices').where({ personal_id: user.id, personal_type: user.type, unique_id: device }).update({
539
- two_factor_approved: true
540
- })
541
-
542
- reply.send({
543
- status: true,
544
- message: 'Two-factor authentication activated'
545
- })
546
- }
547
- }
548
-
549
- const upload_avatar = {
550
- method: 'POST',
551
- path: 'upload-avatar',
552
- schema: {
553
- description: 'Upload avatar',
554
- summary: 'Upload avatar',
555
- body: {
556
- type: 'object',
557
- properties: {
558
- avatar: { type: 'string' }
559
- }
560
- },
561
- success_response: {
562
- type: 'object',
563
- properties: {
564
- status: { type: 'boolean', default: true },
565
- data: { type: 'object', properties: { avatar: { type: 'string' } } }
566
- }
567
- }
568
- },
569
- security: 'auth',
570
- handler: async (request, reply) => {
571
- const user = request.user;
572
- const { avatar } = request.body;
573
-
574
- const upload_result = await uploadFile(avatar, 'avatar', 'users');
575
- if(!upload_result.status) {
576
- return reply.status(400).send({
577
- status: false,
578
- message: 'Failed to upload avatar',
579
- code: 400
580
- });
581
- }
582
-
583
- await db('admins').where('admin_id', user.id).update({
584
- avatar: upload_result.data.file.fileUrl
585
- });
586
-
587
- reply.send({
588
- status: true,
589
- data: { avatar: upload_result.data.file.fileUrl }
590
- })
591
- }
592
- }
593
-
594
- const delete_avatar = {
595
- method: 'DELETE',
596
- path: 'delete-avatar',
597
- schema: {
598
- description: 'Delete avatar',
599
- summary: 'Delete avatar',
600
- success_response: {
601
- type: 'object',
602
- properties: {
603
- status: { type: 'boolean', default: true },
604
- message: { type: 'string' }
605
- }
606
- }
607
- },
608
- security: 'auth',
609
- handler: async (request, reply) => {
610
- const user = request.user;
611
- await db('admins').where('admin_id', user.id).update({
612
- avatar: null
613
- });
614
-
615
- if(user.avatar && fs.existsSync(user.avatar)) {
616
- fs.unlinkSync(user.avatar);
617
- }
618
-
619
- reply.send({
620
- status: true,
621
- message: 'Avatar deleted successfully'
622
- })
623
- }
624
- }
625
-
626
- module.exports = [login, login_2fa, userdata, change_password, update_userdata, upload_avatar, delete_avatar, activate_2fa, activation_2fa,]
@@ -1,3 +0,0 @@
1
- const db = require('@root/libraries/knex');
2
-
3
- module.exports = []