Compare commits
3 Commits
ad12c3a67a
...
36788f34e8
| Author | SHA1 | Date | |
|---|---|---|---|
| 36788f34e8 | |||
| 8ead5a62da | |||
| a8c72b1d64 |
@ -6,9 +6,9 @@
|
||||
"auditLog": ".571b27c4f3dcc376d1f0ca8880ce87cfefd2f30d-audit.json",
|
||||
"files": [
|
||||
{
|
||||
"date": 1738578708264,
|
||||
"name": "debug-2025-02-03.log",
|
||||
"hash": "48ca17f819e391cb5ae1909a6ee0fa4d9c8cdb6667ef893547acb004f4a79737"
|
||||
"date": 1738768744297,
|
||||
"name": "debug-2025-02-05.log",
|
||||
"hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256"
|
||||
}
|
||||
],
|
||||
"hashType": "sha256"
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
"auditLog": ".e6616b1c93d5e50d48b909cd34375b545b447bc6-audit.json",
|
||||
"files": [
|
||||
{
|
||||
"date": 1738578708262,
|
||||
"name": "error-2025-02-03.log",
|
||||
"hash": "a21f154f5c386a75eee98a35c2b100da7df1b8002cf99851b90bd12810f1fe8a"
|
||||
"date": 1738768744292,
|
||||
"name": "error-2025-02-05.log",
|
||||
"hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8"
|
||||
}
|
||||
],
|
||||
"hashType": "sha256"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
{"label":"index.ts","level":"error","message":"Mon, 03 Feb 2025 10:31:54 GMT uncaughtException:","metadata":{"0":"S","1":"e","10":"n","11":"o","12":"t","13":" ","14":"r","15":"u","16":"n","17":"n","18":"i","19":"n","2":"r","20":"g","21":".","3":"v","4":"e","5":"r","6":" ","7":"i","8":"s","9":" "},"timestamp":"2025-02-03T10:31:54.031Z"}
|
||||
{"label":"index.ts","level":"error","message":"Error [ERR_SERVER_NOT_RUNNING]: Server is not running.\n at Server.close (node:net:2356:12)\n at Object.onceWrapper (node:events:638:28)\n at Server.emit (node:events:536:35)\n at emitCloseNT (node:net:2416:8)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)","metadata":{},"timestamp":"2025-02-03T10:31:54.034Z"}
|
||||
{"label":"index.ts","level":"error","message":"Mon, 03 Feb 2025 10:33:04 GMT uncaughtException:","metadata":{"0":"S","1":"e","10":"n","11":"o","12":"t","13":" ","14":"r","15":"u","16":"n","17":"n","18":"i","19":"n","2":"r","20":"g","21":".","3":"v","4":"e","5":"r","6":" ","7":"i","8":"s","9":" "},"timestamp":"2025-02-03T10:33:04.894Z"}
|
||||
{"label":"index.ts","level":"error","message":"Error [ERR_SERVER_NOT_RUNNING]: Server is not running.\n at Server.close (node:net:2356:12)\n at Object.onceWrapper (node:events:638:28)\n at Server.emit (node:events:536:35)\n at emitCloseNT (node:net:2416:8)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)","metadata":{},"timestamp":"2025-02-03T10:33:04.896Z"}
|
||||
{"label":"index.ts","level":"error","message":"Mon, 03 Feb 2025 10:33:49 GMT uncaughtException:","metadata":{"0":"S","1":"e","10":"n","11":"o","12":"t","13":" ","14":"r","15":"u","16":"n","17":"n","18":"i","19":"n","2":"r","20":"g","21":".","3":"v","4":"e","5":"r","6":" ","7":"i","8":"s","9":" "},"timestamp":"2025-02-03T10:33:49.232Z"}
|
||||
{"label":"index.ts","level":"error","message":"Error [ERR_SERVER_NOT_RUNNING]: Server is not running.\n at Server.close (node:net:2356:12)\n at Object.onceWrapper (node:events:638:28)\n at Server.emit (node:events:536:35)\n at emitCloseNT (node:net:2416:8)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)","metadata":{},"timestamp":"2025-02-03T10:33:49.233Z"}
|
||||
{"label":"index.ts","level":"error","message":"Mon, 03 Feb 2025 10:37:14 GMT uncaughtException:","metadata":{"0":"S","1":"e","10":"n","11":"o","12":"t","13":" ","14":"r","15":"u","16":"n","17":"n","18":"i","19":"n","2":"r","20":"g","21":".","3":"v","4":"e","5":"r","6":" ","7":"i","8":"s","9":" "},"timestamp":"2025-02-03T10:37:14.756Z"}
|
||||
{"label":"index.ts","level":"error","message":"Error [ERR_SERVER_NOT_RUNNING]: Server is not running.\n at Server.close (node:net:2356:12)\n at Object.onceWrapper (node:events:638:28)\n at Server.emit (node:events:536:35)\n at emitCloseNT (node:net:2416:8)\n at process.processTicksAndRejections (node:internal/process/task_queues:89:21)","metadata":{},"timestamp":"2025-02-03T10:37:14.758Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","code":"ERR_SERVER_NOT_RUNNING"},"timestamp":"2025-02-03T10:47:26.394Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","code":"ERR_SERVER_NOT_RUNNING"},"timestamp":"2025-02-03T10:47:46.769Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","code":"ERR_SERVER_NOT_RUNNING"},"timestamp":"2025-02-03T10:56:00.612Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:08:06.568Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:08:30.574Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","code":"ERR_SERVER_NOT_RUNNING"},"timestamp":"2025-02-03T11:09:05.310Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:14:03.193Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:20:47.049Z"}
|
||||
{"label":"index.ts","level":"error","message":"Could not close connections in time, forcefully shutting down","metadata":{},"timestamp":"2025-02-03T11:21:20.528Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:22:22.098Z"}
|
||||
{"label":"index.ts","level":"error","message":"Could not close connections in time, forcefully shutting down","metadata":{},"timestamp":"2025-02-03T11:29:22.891Z"}
|
||||
{"label":"index.ts","level":"error","message":"Could not close connections in time, forcefully shutting down","metadata":{},"timestamp":"2025-02-03T11:30:14.987Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":","name":"SequelizeDatabaseError","original":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"parameters":{},"parent":{"code":"ER_NO_SUCH_TABLE","errno":1146,"fatal":false,"name":"SqlError","sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');","sqlMessage":"Table 'uecko_erp.users' doesn't exist","sqlState":"42S02"},"sql":"SELECT `id`, `username`, `email`, `password`, `roles`, `isActive`, `created_at`, `updated_at`, `deleted_at` FROM `users` AS `AuthUserModel` WHERE (`AuthUserModel`.`deleted_at` IS NULL AND `AuthUserModel`.`id` = 'email');"},"timestamp":"2025-02-03T11:39:04.414Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T13:05:42.332Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: value.join is not a function","metadata":{},"timestamp":"2025-02-03T15:59:05.262Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T15:59:05.277Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: value.join is not a function","metadata":{},"timestamp":"2025-02-03T15:59:35.470Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T15:59:35.473Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: value.join is not a function","metadata":{},"timestamp":"2025-02-03T15:59:48.809Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T15:59:48.811Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: value.join is not a function","metadata":{},"timestamp":"2025-02-03T16:01:28.918Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T16:01:28.932Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: value.join is not a function","metadata":{},"timestamp":"2025-02-03T16:02:07.671Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T16:02:07.678Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T16:05:54.782Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:06:09.336Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:06:09.345Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:29:22.183Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:29:22.193Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:29:25.843Z"}
|
||||
{"label":"index.ts","level":"error","message":"[500] Internal Server Error: Unexpected database error","metadata":{},"timestamp":"2025-02-03T16:29:25.848Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:29:58.465Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:29:58.471Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:30:58.651Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:30:58.659Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T16:31:11.297Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:31:39.443Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:31:39.446Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T16:42:21.737Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T16:53:04.645Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T16:53:04.652Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T16:53:24.919Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled Rejection at:","metadata":{"0":"r","1":"e","2":"a","3":"s","4":"o","5":"n","6":":"},"timestamp":"2025-02-03T16:53:59.783Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T17:53:46.420Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T17:53:46.434Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Validation Error","metadata":{},"timestamp":"2025-02-03T18:09:57.217Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Validation Error","metadata":{},"timestamp":"2025-02-03T18:10:42.558Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:34:31.991Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:36:02.667Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:44:04.627Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:44:24.719Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:46:29.064Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T18:46:40.967Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T19:07:24.350Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T19:07:24.352Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"local-jwt\"","metadata":{},"timestamp":"2025-02-03T19:09:15.070Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T19:09:23.879Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:635, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:50:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:109:5"},"timestamp":"2025-02-03T19:24:23.645Z"}
|
||||
{"label":"index.ts","level":"error","message":"Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T19:45:07.427Z"}
|
||||
{"label":"index.ts","level":"error","message":"💥 Unhandled API error: Unknown authentication strategy \"jwt\"","metadata":{},"timestamp":"2025-02-03T19:46:17.959Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:823, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:43:21.049Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:827, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:43:23.751Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:829, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:43:37.528Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:833, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:43:44.126Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:835, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:44:09.809Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:839, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:42:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:46:33.069Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:842, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:43:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:46:50.035Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Error synchronizing database: (conn:845, no: 1069, SQLState: 42000) Too many keys specified; max 64 keys allowed\nsql: ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE; - parameters:[]","metadata":{"name":"SequelizeDatabaseError","original":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"parameters":{},"parent":{"code":"ER_TOO_MANY_KEYS","errno":1069,"fatal":false,"name":"SqlError","sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","sqlMessage":"Too many keys specified; max 64 keys allowed","sqlState":"42000"},"sql":"ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) NOT NULL UNIQUE;","stack":"Error: \n at Query.run (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/dialects/mariadb/query.js:47:25)\n at /home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:650:28\n at processTicksAndRejections (node:internal/process/task_queues:105:5)\n at Function.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/model.js:1408:11)\n at Sequelize.sync (/home/rodax/Documentos/uecko-erp/node_modules/.pnpm/sequelize@6.37.5_mariadb@3.4.0_mysql2@3.12.0/node_modules/sequelize/src/sequelize.js:825:9)\n at registerModels (/home/rodax/Documentos/uecko-erp/apps/server/src/config/register-models.ts:57:7)\n at connectToDatabase (/home/rodax/Documentos/uecko-erp/apps/server/src/config/database.ts:43:5)\n at /home/rodax/Documentos/uecko-erp/apps/server/src/index.ts:115:5"},"timestamp":"2025-02-03T21:47:08.690Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Unhandled API error: createLoginController is not defined","metadata":{},"timestamp":"2025-02-03T21:50:39.274Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Unhandled API error: createLoginController is not defined","metadata":{},"timestamp":"2025-02-03T21:52:51.178Z"}
|
||||
{"label":"index.ts","level":"error","message":"❌ Unhandled API error: createLoginController is not defined","metadata":{},"timestamp":"2025-02-03T21:53:12.086Z"}
|
||||
{"label":"index.ts","level":"error","message":"[503] Service Unavailable: Invalid email or password","metadata":{},"timestamp":"2025-02-03T21:53:25.527Z"}
|
||||
{"label":"index.ts","level":"error","message":"[503] Service Unavailable: Invalid email or password","metadata":{},"timestamp":"2025-02-03T21:53:36.301Z"}
|
||||
{"label":"index.ts","level":"error","message":"[503] Service Unavailable: Invalid email or password","metadata":{},"timestamp":"2025-02-03T21:53:39.351Z"}
|
||||
{"label":"index.ts","level":"error","message":"[401] Unauthorized: Invalid email or password","metadata":{},"timestamp":"2025-02-03T21:53:53.504Z"}
|
||||
{"label":"index.ts","level":"error","message":"Database error: Validation error","metadata":{},"timestamp":"2025-02-03T21:54:31.584Z"}
|
||||
{"label":"index.ts","level":"error","message":"[409] Conflict: User with this email already exists","metadata":{},"timestamp":"2025-02-03T21:54:31.587Z"}
|
||||
{"label":"index.ts","level":"error","message":"[401] Unauthorized: Invalid email or password","metadata":{},"timestamp":"2025-02-03T21:54:42.181Z"}
|
||||
@ -1,5 +1,6 @@
|
||||
import { logger } from "@common/infrastructure/logger";
|
||||
import { globalErrorHandler } from "@common/presentation";
|
||||
import { initializePassportAuthProvide } from "@contexts/auth/infraestructure";
|
||||
import dotenv from "dotenv";
|
||||
import express, { Application } from "express";
|
||||
import helmet from "helmet";
|
||||
@ -24,7 +25,10 @@ export function createApp(): Application {
|
||||
app.use(responseTime()); // set up the response-time middleware
|
||||
|
||||
// Inicializar Passport
|
||||
//app.use((req, res, next) => createPassportAuthProvider());
|
||||
app.use((req, res, next) => {
|
||||
initializePassportAuthProvide();
|
||||
next();
|
||||
});
|
||||
|
||||
app.use((req, _, next) => {
|
||||
logger.info(`▶️ Incoming request ${req.method} to ${req.path}`);
|
||||
|
||||
@ -46,19 +46,27 @@ export class Result<T, E extends Error = Error> {
|
||||
}
|
||||
|
||||
get data(): T {
|
||||
if (!this._isSuccess) {
|
||||
throw new Error("Cannot get value data from a failed result.");
|
||||
}
|
||||
return this._data as T;
|
||||
return this.getData();
|
||||
}
|
||||
|
||||
get error(): E {
|
||||
return this.getError();
|
||||
}
|
||||
|
||||
getError(): E {
|
||||
if (this._isSuccess) {
|
||||
throw new Error("Cannot get error from a successful result.");
|
||||
}
|
||||
return this._error as E;
|
||||
}
|
||||
|
||||
getData(): T {
|
||||
if (!this._isSuccess) {
|
||||
throw new Error("Cannot get value data from a failed result.");
|
||||
}
|
||||
return this._data as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔹 `getOrElse(defaultValue: T): T`
|
||||
* Si el `Result` es un `ok`, devuelve `data`, de lo contrario, devuelve `defaultValue`.
|
||||
|
||||
@ -1,35 +1,54 @@
|
||||
import { UniqueID } from "./value-objects/unique-id";
|
||||
import { UniqueID } from "./unique-id";
|
||||
|
||||
describe("UniqueID Value Object", () => {
|
||||
it("should generate a new UUID using generateNewID()", () => {
|
||||
const result = UniqueID.generateNewID();
|
||||
// Mock UUID generation to ensure predictable tests
|
||||
jest.mock("uuid", () => ({ v4: () => "123e4567-e89b-12d3-a456-426614174000" }));
|
||||
|
||||
describe("UniqueID", () => {
|
||||
test("should create a UniqueID with a valid UUID", () => {
|
||||
const id = "123e4567-e89b-12d3-a456-426614174000";
|
||||
const result = UniqueID.create(id);
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data.getValue()).toMatch(
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
);
|
||||
expect(result.data?.isDefined()).toBe(true);
|
||||
});
|
||||
|
||||
it("should return an error for an invalid UUID", () => {
|
||||
test("should fail to create UniqueID with an invalid UUID", () => {
|
||||
const result = UniqueID.create("invalid-uuid");
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.error.message).toBe("Invalid UUID format");
|
||||
expect(result.isFailure).toBe(true);
|
||||
});
|
||||
|
||||
it("should create a valid UniqueID from an existing UUID", () => {
|
||||
const validUUID = "550e8400-e29b-41d4-a716-446655440000";
|
||||
const result = UniqueID.create(validUUID);
|
||||
test("should create an undefined UniqueID when id is undefined and generateOnEmpty is false", () => {
|
||||
const result = UniqueID.create(undefined, false);
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data.getValue()).toBe(validUUID);
|
||||
expect(result.data?.isDefined()).toBe(false);
|
||||
});
|
||||
|
||||
it("should correctly convert UniqueID to string", () => {
|
||||
const validUUID = "550e8400-e29b-41d4-a716-446655440000";
|
||||
const result = UniqueID.create(validUUID);
|
||||
test("should generate a new UUID when id is undefined and generateOnEmpty is true", () => {
|
||||
const result = UniqueID.create(undefined, true);
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data.toString()).toBe(validUUID);
|
||||
expect(result.data?.isDefined()).toBe(true);
|
||||
});
|
||||
|
||||
test("should fail when id is null", () => {
|
||||
const result = UniqueID.create(null as any);
|
||||
|
||||
expect(result.isFailure).toBe(true);
|
||||
});
|
||||
|
||||
test("should create a UniqueID when id is an empty string and generateOnEmpty is true", () => {
|
||||
const result = UniqueID.create(" ", true);
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data?.isDefined()).toBe(true);
|
||||
});
|
||||
|
||||
test("should create an undefined UniqueID when id is an empty string and generateOnEmpty is false", () => {
|
||||
const result = UniqueID.create(" ", false);
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data?.isDefined()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,17 +3,27 @@ import { z } from "zod";
|
||||
import { Result } from "../result";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
const UUIDSchema = z.string().uuid({ message: "Invalid UUID format" });
|
||||
export const UNDEFINED_ID = undefined;
|
||||
|
||||
export class UniqueID extends ValueObject<string | undefined> {
|
||||
protected readonly _hasId!: boolean;
|
||||
|
||||
protected constructor(id?: string) {
|
||||
super(id);
|
||||
this._hasId = id != UNDEFINED_ID;
|
||||
}
|
||||
|
||||
export class UniqueID extends ValueObject<string> {
|
||||
static create(id?: string, generateOnEmpty: boolean = false): Result<UniqueID, Error> {
|
||||
if (!id) {
|
||||
return generateOnEmpty
|
||||
? UniqueID.generateNewID()
|
||||
: Result.fail(new Error("ID is null or empty"));
|
||||
if (id === null) {
|
||||
return Result.fail(new Error("ID cannot be null"));
|
||||
}
|
||||
|
||||
const result = UniqueID.validate(id.trim());
|
||||
const trimmedId = id?.trim();
|
||||
if (!trimmedId) {
|
||||
return generateOnEmpty ? UniqueID.generateNewID() : Result.ok(new UniqueID(UNDEFINED_ID));
|
||||
}
|
||||
|
||||
const result = UniqueID.validate(trimmedId);
|
||||
|
||||
return result.success
|
||||
? Result.ok(new UniqueID(result.data))
|
||||
@ -32,4 +42,12 @@ export class UniqueID extends ValueObject<string> {
|
||||
static generateNewID(): Result<UniqueID, never> {
|
||||
return Result.ok(new UniqueID(uuidv4()));
|
||||
}
|
||||
|
||||
static generateUndefinedID(): Result<UniqueID, never> {
|
||||
return Result.ok(new UniqueID(UNDEFINED_ID));
|
||||
}
|
||||
|
||||
isDefined(): boolean {
|
||||
return this._hasId;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,8 +5,6 @@ export abstract class ValueObject<T> {
|
||||
|
||||
protected constructor(value: T) {
|
||||
this._value = typeof value === "object" && value !== null ? Object.freeze(value) : value;
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
equals(other: ValueObject<T>): boolean {
|
||||
|
||||
@ -15,6 +15,36 @@ export abstract class SequelizeRepository<T> implements IAggregateRootRepository
|
||||
});
|
||||
}
|
||||
|
||||
protected async _getBy(
|
||||
model: ModelDefined<any, any>,
|
||||
field: string,
|
||||
value: any,
|
||||
params: any = {},
|
||||
transaction?: Transaction
|
||||
): Promise<any> {
|
||||
const where: { [key: string]: any } = {};
|
||||
|
||||
where[field] = value;
|
||||
|
||||
return model.findOne({
|
||||
where,
|
||||
transaction,
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
protected async _getById(
|
||||
model: ModelDefined<any, any>,
|
||||
id: UniqueID | string,
|
||||
params: any = {},
|
||||
transaction?: Transaction
|
||||
): Promise<any> {
|
||||
return model.findByPk(id.toString(), {
|
||||
transaction,
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
protected async _exists(
|
||||
model: ModelDefined<any, any>,
|
||||
field: string,
|
||||
|
||||
@ -51,8 +51,9 @@ export abstract class ExpressController {
|
||||
return ExpressController.errorResponse(
|
||||
new ApiError({
|
||||
status: 401,
|
||||
title: "Unauthorized",
|
||||
detail: message ?? "You are not authorized to access this resource.",
|
||||
title: httpStatus["401"],
|
||||
name: httpStatus["401_NAME"],
|
||||
detail: message ?? httpStatus["401_MESSAGE"],
|
||||
}),
|
||||
this.res
|
||||
);
|
||||
@ -108,8 +109,9 @@ export abstract class ExpressController {
|
||||
return ExpressController.errorResponse(
|
||||
new ApiError({
|
||||
status: 422,
|
||||
title: "Invalid Input",
|
||||
detail: message,
|
||||
title: httpStatus["422"],
|
||||
name: httpStatus["422_NAME"],
|
||||
detail: message ?? httpStatus["422_MESSAGE"],
|
||||
errors,
|
||||
}),
|
||||
this.res
|
||||
|
||||
@ -2,10 +2,10 @@ import { AuthenticatedUser } from "../domain";
|
||||
|
||||
export interface IAuthProvider {
|
||||
/* JWT Strategy */
|
||||
generateAccessToken(payload: any): string;
|
||||
generateRefreshToken(payload: any): string;
|
||||
verifyToken(token: string): any;
|
||||
generateAccessToken(payload: object): string;
|
||||
generateRefreshToken(payload: object): string;
|
||||
verifyToken(token: string): Promise<AuthenticatedUser | null>;
|
||||
|
||||
/* LocalStrategy */
|
||||
verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>;
|
||||
//_verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>;
|
||||
}
|
||||
|
||||
@ -1,15 +1,27 @@
|
||||
import { Result } from "@common/domain";
|
||||
import { AuthenticatedUser, EmailAddress, PasswordHash, Username } from "../domain";
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain";
|
||||
|
||||
export interface IAuthService {
|
||||
registerUser(params: {
|
||||
username: Username;
|
||||
email: EmailAddress;
|
||||
passwordHash: PasswordHash;
|
||||
hashPassword: HashPassword;
|
||||
}): Promise<Result<AuthenticatedUser, Error>>;
|
||||
|
||||
loginUser(params: {
|
||||
email: EmailAddress;
|
||||
passwordHash: PasswordHash;
|
||||
}): Promise<Result<AuthenticatedUser, Error>>;
|
||||
plainPassword: PlainPassword;
|
||||
tabId: UniqueID;
|
||||
}): Promise<
|
||||
Result<
|
||||
{
|
||||
user: AuthenticatedUser;
|
||||
tokens: {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
},
|
||||
Error
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
@ -3,24 +3,29 @@ import { ITransactionManager } from "@common/infrastructure/database";
|
||||
import {
|
||||
AuthenticatedUser,
|
||||
EmailAddress,
|
||||
HashPassword,
|
||||
IAuthenticatedUserRepository,
|
||||
PasswordHash,
|
||||
TabContext,
|
||||
Username,
|
||||
} from "@contexts/auth/domain";
|
||||
} from "../domain";
|
||||
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
|
||||
import { IAuthProvider } from "./auth-provider.interface";
|
||||
import { IAuthService } from "./auth-service.interface";
|
||||
|
||||
export class AuthService implements IAuthService {
|
||||
private readonly _respository!: IAuthenticatedUserRepository;
|
||||
private readonly _userRepo!: IAuthenticatedUserRepository;
|
||||
private readonly _tabContactRepo!: ITabContextRepository;
|
||||
private readonly _transactionManager!: ITransactionManager;
|
||||
private readonly _authProvider: IAuthProvider;
|
||||
|
||||
constructor(
|
||||
repository: IAuthenticatedUserRepository,
|
||||
userRepo: IAuthenticatedUserRepository,
|
||||
tabContextRepo: ITabContextRepository,
|
||||
transactionManager: ITransactionManager,
|
||||
authProvider: IAuthProvider
|
||||
) {
|
||||
this._respository = repository;
|
||||
this._userRepo = userRepo;
|
||||
this._tabContactRepo = tabContextRepo;
|
||||
this._transactionManager = transactionManager;
|
||||
this._authProvider = authProvider;
|
||||
}
|
||||
@ -32,29 +37,25 @@ export class AuthService implements IAuthService {
|
||||
async registerUser(params: {
|
||||
username: Username;
|
||||
email: EmailAddress;
|
||||
passwordHash: PasswordHash;
|
||||
hashPassword: HashPassword;
|
||||
}): Promise<Result<AuthenticatedUser, Error>> {
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
const { username, email, passwordHash } = params;
|
||||
const { username, email, hashPassword } = params;
|
||||
|
||||
// Verificar si el usuario ya existe
|
||||
const userExists = await this._respository.findUserByEmail(email, transaction);
|
||||
const userExists = await this._userRepo.userExists(email, transaction);
|
||||
if (userExists.isSuccess && userExists.data) {
|
||||
return Result.fail(new Error("Email is already registered"));
|
||||
}
|
||||
|
||||
if (userExists.isFailure) {
|
||||
return Result.fail(userExists.error);
|
||||
}
|
||||
|
||||
const newUserId = UniqueID.generateNewID().data;
|
||||
|
||||
const userOrError = AuthenticatedUser.create(
|
||||
{
|
||||
username,
|
||||
email,
|
||||
passwordHash,
|
||||
hashPassword,
|
||||
roles: ["USER"],
|
||||
},
|
||||
newUserId
|
||||
@ -64,7 +65,7 @@ export class AuthService implements IAuthService {
|
||||
return Result.fail(userOrError.error);
|
||||
}
|
||||
|
||||
const createdResult = await this._respository.createUser(userOrError.data, transaction);
|
||||
const createdResult = await this._userRepo.createUser(userOrError.data, transaction);
|
||||
|
||||
if (createdResult.isFailure) {
|
||||
return Result.fail(createdResult.error);
|
||||
@ -83,79 +84,79 @@ export class AuthService implements IAuthService {
|
||||
*/
|
||||
async loginUser(params: {
|
||||
email: EmailAddress;
|
||||
passwordHash: PasswordHash;
|
||||
}): Promise<Result<AuthenticatedUser, Error>> {
|
||||
plainPassword: HashPassword;
|
||||
tabId: UniqueID;
|
||||
}): Promise<
|
||||
Result<
|
||||
{
|
||||
user: AuthenticatedUser;
|
||||
tokens: {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
},
|
||||
Error
|
||||
>
|
||||
> {
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
const { email, passwordHash } = params;
|
||||
const { email, plainPassword, tabId } = params;
|
||||
|
||||
// Verificar que el tab ID está definido
|
||||
if (!tabId.isDefined()) {
|
||||
return Result.fail(new Error("Invalid tab id"));
|
||||
}
|
||||
|
||||
// 🔹 Verificar si el usuario existe en la base de datos
|
||||
const userResult = await this._respository.findUserByEmail(email, transaction);
|
||||
const userResult = await this._userRepo.getUserByEmail(email, transaction);
|
||||
if (userResult.isFailure) {
|
||||
return Result.fail(new Error("Invalid email or password"));
|
||||
}
|
||||
|
||||
const user = userResult.data;
|
||||
if (!user) {
|
||||
return Result.fail(new Error("Invalid email or password"));
|
||||
}
|
||||
|
||||
// 🔹 Verificar que la contraseña sea correcta
|
||||
const isValidPassword = await user.comparePassword(passwordHash);
|
||||
const isValidPassword = await user.verifyPassword(plainPassword);
|
||||
if (!isValidPassword) {
|
||||
return Result.fail(new Error("Invalid email or password"));
|
||||
}
|
||||
|
||||
// Registrar o actualizar el contexto de ese tab ID
|
||||
const contextOrError = TabContext.create({
|
||||
userId: user.id,
|
||||
tabId: tabId,
|
||||
companyId: UniqueID.generateUndefinedID().data,
|
||||
branchId: UniqueID.generateUndefinedID().data,
|
||||
});
|
||||
|
||||
if (contextOrError.isFailure) {
|
||||
return Result.fail(new Error("Error creating user context"));
|
||||
}
|
||||
|
||||
await this._tabContactRepo.registerContext(contextOrError.data, transaction);
|
||||
|
||||
// 🔹 Generar Access Token y Refresh Token
|
||||
user.accessToken = this._authProvider.generateAccessToken({
|
||||
const accessToken = this._authProvider.generateAccessToken({
|
||||
userId: user.id.toString(),
|
||||
email: email.toString(),
|
||||
tabId: tabId.toString(),
|
||||
roles: ["USER"],
|
||||
});
|
||||
|
||||
user.refreshToken = this._authProvider.generateRefreshToken({
|
||||
const refreshToken = this._authProvider.generateRefreshToken({
|
||||
userId: user.id.toString(),
|
||||
});
|
||||
|
||||
return Result.ok(user);
|
||||
return Result.ok({
|
||||
user,
|
||||
tokens: {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
},
|
||||
});
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔹 `selectCompany`
|
||||
* Permite a un usuario seleccionar una empresa activa en la sesión bajo transacción.
|
||||
*/
|
||||
/*static async selectCompany(
|
||||
userId: string,
|
||||
companyId: string
|
||||
): Promise<Result<{ message: string }, Error>> {
|
||||
return await authUserRepository.executeTransaction(async (transaction) => {
|
||||
const user = await authUserRepository.findById(userId, transaction);
|
||||
if (user.isFailure) {
|
||||
return Result.fail(new Error("User not found"));
|
||||
}
|
||||
|
||||
const isAssociated = await authUserRepository.isUserAssociatedWithCompany(
|
||||
userId,
|
||||
companyId,
|
||||
transaction
|
||||
);
|
||||
if (!isAssociated) {
|
||||
return Result.fail(new Error("User does not have access to this company"));
|
||||
}
|
||||
|
||||
return Result.ok({ message: "Company selected successfully" });
|
||||
});
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 🔹 `logout`
|
||||
* Simula el cierre de sesión de un usuario. No requiere transacción.
|
||||
*/
|
||||
/*static logout(): Result<{ message: string }, never> {
|
||||
return Result.ok({ message: "Logged out successfully" });
|
||||
}*/
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { createSequelizeTransactionManager } from "@common/infrastructure";
|
||||
|
||||
import { createAuthenticatedUserRepository } from "../infraestructure";
|
||||
import { createAuthenticatedUserRepository, createTabContextRepository } from "../infraestructure";
|
||||
import { createPassportAuthProvider } from "../infraestructure/passport/passport-auth-provider";
|
||||
import { IAuthProvider } from "./auth-provider.interface";
|
||||
import { IAuthService } from "./auth-service.interface";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { ITabContextService } from "./tab-context-service.interface";
|
||||
import { TabContextService } from "./tab-context.service";
|
||||
|
||||
export * from "./auth-provider.interface";
|
||||
export * from "./auth-service.interface";
|
||||
@ -12,10 +14,23 @@ export * from "./auth-service.interface";
|
||||
export const createAuthService = (): IAuthService => {
|
||||
const transactionManager = createSequelizeTransactionManager();
|
||||
const authenticatedUserRepository = createAuthenticatedUserRepository();
|
||||
const tabContextRepository = createTabContextRepository();
|
||||
|
||||
const authProvider: IAuthProvider = createPassportAuthProvider(
|
||||
authenticatedUserRepository,
|
||||
transactionManager
|
||||
);
|
||||
return new AuthService(authenticatedUserRepository, transactionManager, authProvider);
|
||||
return new AuthService(
|
||||
authenticatedUserRepository,
|
||||
tabContextRepository,
|
||||
transactionManager,
|
||||
authProvider
|
||||
);
|
||||
};
|
||||
|
||||
export const createTabContextService = (): ITabContextService => {
|
||||
const transactionManager = createSequelizeTransactionManager();
|
||||
const tabContextRepository = createTabContextRepository();
|
||||
|
||||
return new TabContextService(tabContextRepository, transactionManager);
|
||||
};
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { TabContext } from "../domain";
|
||||
|
||||
export interface ITabContextService {
|
||||
getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>;
|
||||
createContext(params: {
|
||||
tabId: UniqueID;
|
||||
userId: UniqueID;
|
||||
companyId: UniqueID;
|
||||
branchId: UniqueID;
|
||||
}): Promise<Result<TabContext, Error>>;
|
||||
assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>>;
|
||||
removeContext(tabId: UniqueID): Promise<Result<void, Error>>;
|
||||
}
|
||||
117
apps/server/src/contexts/auth/application/tab-context.service.ts
Normal file
117
apps/server/src/contexts/auth/application/tab-context.service.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { ITransactionManager } from "@common/infrastructure/database";
|
||||
import { TabContext } from "../domain";
|
||||
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
|
||||
import { ITabContextService } from "./tab-context-service.interface";
|
||||
|
||||
export class TabContextService implements ITabContextService {
|
||||
private readonly _respository!: ITabContextRepository;
|
||||
private readonly _transactionManager!: ITransactionManager;
|
||||
|
||||
constructor(repository: ITabContextRepository, transactionManager: ITransactionManager) {
|
||||
this._respository = repository;
|
||||
this._transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el contexto de una pestaña por su ID
|
||||
*/
|
||||
async getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>> {
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
// Verificar si la pestaña existe
|
||||
const tabContextOrError = await this._respository.getContextByTabId(tabId, transaction);
|
||||
if (tabContextOrError.isSuccess && !tabContextOrError.data) {
|
||||
return Result.fail(new Error("Invalid or expired Tab ID"));
|
||||
}
|
||||
|
||||
if (tabContextOrError.isFailure) {
|
||||
return Result.fail(tabContextOrError.error);
|
||||
}
|
||||
|
||||
return Result.ok(tabContextOrError.data);
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra un nuevo contexto de pestaña para un usuario
|
||||
*/
|
||||
async createContext(params: {
|
||||
tabId: UniqueID;
|
||||
userId: UniqueID;
|
||||
companyId: UniqueID;
|
||||
branchId: UniqueID;
|
||||
}): Promise<Result<TabContext, Error>> {
|
||||
const { tabId, userId, companyId, branchId } = params;
|
||||
|
||||
if (!userId || !tabId) {
|
||||
return Result.fail(new Error("User ID and Tab ID are required"));
|
||||
}
|
||||
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
const contextOrError = TabContext.create(
|
||||
{
|
||||
userId,
|
||||
tabId,
|
||||
companyId,
|
||||
branchId,
|
||||
},
|
||||
UniqueID.generateNewID().data
|
||||
);
|
||||
|
||||
if (contextOrError.isFailure) {
|
||||
return Result.fail(contextOrError.error);
|
||||
}
|
||||
|
||||
await this._respository.registerContext(contextOrError.data, transaction);
|
||||
|
||||
return Result.ok(contextOrError.data);
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asigna una empresa activa a un contexto de pestaña
|
||||
*/
|
||||
async assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>> {
|
||||
if (!companyId || !tabId) {
|
||||
return Result.fail(new Error("Tab ID and Company ID are required"));
|
||||
}
|
||||
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
// Verificar si la pestaña existe
|
||||
const tabContextOrError = await this._respository.contextExists(tabId, transaction);
|
||||
if (tabContextOrError.isFailure || !tabContextOrError.data) {
|
||||
return Result.fail(new Error("Invalid or expired Tab ID"));
|
||||
}
|
||||
|
||||
return await this._respository.updateCompanyByTabId(tabId, companyId, transaction);
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina un contexto de pestaña por su ID
|
||||
*/
|
||||
async removeContext(tabId: UniqueID): Promise<Result<void, Error>> {
|
||||
if (!tabId) {
|
||||
return Result.fail(new Error("Tab ID is required"));
|
||||
}
|
||||
try {
|
||||
return await this._transactionManager.complete(async (transaction) => {
|
||||
return await this._respository.deleteContextByTabId(tabId, transaction);
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import { AggregateRoot, Result, UniqueID } from "@common/domain";
|
||||
import { UserAuthenticatedEvent } from "../events";
|
||||
import { EmailAddress, PasswordHash, Username } from "../value-objects";
|
||||
import { EmailAddress, HashPassword, PlainPassword, Username } from "../value-objects";
|
||||
|
||||
export interface IAuthenticatedUserProps {
|
||||
username: Username;
|
||||
email: EmailAddress;
|
||||
passwordHash: PasswordHash;
|
||||
hashPassword: HashPassword;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
@ -19,7 +19,9 @@ export interface IAuthenticatedUser {
|
||||
isUser: boolean;
|
||||
isAdmin: boolean;
|
||||
|
||||
comparePassword(password: PasswordHash | string): Promise<boolean>;
|
||||
contexts: ICollection<QuoteItem>;
|
||||
|
||||
verifyPassword(candidatePassword: PlainPassword): Promise<boolean>;
|
||||
getRoles(): string[];
|
||||
toPersistenceData(): any;
|
||||
}
|
||||
@ -45,12 +47,8 @@ export class AuthenticatedUser
|
||||
return (this._props.roles || []).some((r) => r === role);
|
||||
}
|
||||
|
||||
comparePassword(password: PasswordHash | string): Promise<boolean> {
|
||||
if (typeof password === "string") {
|
||||
return this._props.passwordHash.compare(password);
|
||||
} else {
|
||||
return this._props.passwordHash.compare(password.toString());
|
||||
}
|
||||
verifyPassword(candidatePassword: PlainPassword): Promise<boolean> {
|
||||
return this._props.hashPassword.verifyPassword(candidatePassword.toString());
|
||||
}
|
||||
|
||||
getRoles(): string[] {
|
||||
@ -81,10 +79,10 @@ export class AuthenticatedUser
|
||||
id: this._id.toString(),
|
||||
username: this._props.username.toString(),
|
||||
email: this._props.email.toString(),
|
||||
password: this._props.passwordHash.toString(),
|
||||
hash_password: this._props.hashPassword.toString(),
|
||||
roles: this._props.roles.map((role) => role.toString()),
|
||||
accessToken: this.accessToken,
|
||||
refreshToken: this.refreshToken,
|
||||
access_token: this.accessToken,
|
||||
refresh_token: this.refreshToken,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
1
apps/server/src/contexts/auth/domain/entities/index.ts
Normal file
1
apps/server/src/contexts/auth/domain/entities/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./tab-context";
|
||||
63
apps/server/src/contexts/auth/domain/entities/tab-context.ts
Normal file
63
apps/server/src/contexts/auth/domain/entities/tab-context.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { DomainEntity, Result, UniqueID } from "@common/domain";
|
||||
|
||||
export interface ITabContextProps {
|
||||
tabId: UniqueID;
|
||||
userId: UniqueID;
|
||||
companyId: UniqueID;
|
||||
branchId: UniqueID;
|
||||
}
|
||||
|
||||
export interface ITabContext {
|
||||
tabId: UniqueID;
|
||||
userId: UniqueID;
|
||||
companyId: UniqueID;
|
||||
branchId: UniqueID;
|
||||
|
||||
hasCompanyAssigned(): boolean;
|
||||
hasBranchAssigned(): boolean;
|
||||
|
||||
toPersistenceData(): any;
|
||||
}
|
||||
|
||||
export class TabContext extends DomainEntity<ITabContextProps> implements ITabContext {
|
||||
static create(props: ITabContextProps, id?: UniqueID): Result<TabContext, Error> {
|
||||
return Result.ok(new this(props, id));
|
||||
}
|
||||
|
||||
get tabId(): UniqueID {
|
||||
return this._props.tabId;
|
||||
}
|
||||
|
||||
get userId(): UniqueID {
|
||||
return this._props.userId;
|
||||
}
|
||||
|
||||
get companyId(): UniqueID {
|
||||
return this._props.companyId;
|
||||
}
|
||||
|
||||
get branchId(): UniqueID {
|
||||
return this._props.branchId;
|
||||
}
|
||||
|
||||
hasCompanyAssigned(): boolean {
|
||||
return this._props.companyId.isDefined();
|
||||
}
|
||||
|
||||
hasBranchAssigned(): boolean {
|
||||
return this._props.branchId.isDefined();
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔹 Devuelve una representación lista para persistencia
|
||||
*/
|
||||
toPersistenceData(): any {
|
||||
return {
|
||||
id: this._id.toString(),
|
||||
tab_id: this.tabId.toString(),
|
||||
user_id: this.userId.toString(),
|
||||
company_id: this.companyId.toString(),
|
||||
branch_id: this.branchId.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./aggregates/authenticated-user";
|
||||
export * from "./events/user-authenticated.event";
|
||||
export * from "./aggregates";
|
||||
export * from "./entities";
|
||||
export * from "./events";
|
||||
export * from "./repositories";
|
||||
export * from "./value-objects";
|
||||
|
||||
@ -3,10 +3,7 @@ import { AuthenticatedUser } from "../aggregates";
|
||||
import { EmailAddress } from "../value-objects";
|
||||
|
||||
export interface IAuthenticatedUserRepository {
|
||||
findUserByEmail(
|
||||
email: EmailAddress,
|
||||
transaction?: any
|
||||
): Promise<Result<AuthenticatedUser | null, Error>>;
|
||||
|
||||
getUserByEmail(email: EmailAddress, transaction?: any): Promise<Result<AuthenticatedUser, Error>>;
|
||||
userExists(email: EmailAddress, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
createUser(user: AuthenticatedUser, transaction?: any): Promise<Result<void, Error>>;
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { Transaction } from "sequelize";
|
||||
import { TabContext } from "../entities";
|
||||
|
||||
export interface ITabContextRepository {
|
||||
getContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<TabContext, Error>>;
|
||||
registerContext(context: TabContext, transaction?: Transaction): Promise<Result<void, Error>>;
|
||||
contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
updateCompanyByTabId(
|
||||
tabId: UniqueID,
|
||||
companyId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<void, Error>>;
|
||||
deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>>;
|
||||
}
|
||||
@ -1,33 +1,33 @@
|
||||
import { PasswordHash } from "./password-hash";
|
||||
import { HashPassword } from "./hash-password";
|
||||
|
||||
describe("PasswordHash Value Object", () => {
|
||||
it("should hash a valid password", async () => {
|
||||
const result = await PasswordHash.create("StrongPass123");
|
||||
const result = HashPassword.create("StrongPass123");
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.data.getValue()).not.toBe("StrongPass123"); // Should be hashed
|
||||
});
|
||||
|
||||
it("should return an error for short password", async () => {
|
||||
const result = await PasswordHash.create("12345");
|
||||
const result = HashPassword.create("12345");
|
||||
|
||||
expect(result.isSuccess).toBe(true);
|
||||
expect(result.error.message).toBe("Password must be at least 6 characters long");
|
||||
});
|
||||
|
||||
it("should validate password comparison correctly", async () => {
|
||||
const result = await PasswordHash.create("SecurePass123");
|
||||
const result = HashPassword.create("SecurePass123");
|
||||
expect(result.isSuccess).toBe(true);
|
||||
|
||||
const isValid = await result.data.compare("SecurePass123");
|
||||
const isValid = await result.data.verifyPassword("SecurePass123");
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail password comparison for incorrect passwords", async () => {
|
||||
const result = await PasswordHash.create("SecurePass123");
|
||||
const result = HashPassword.create("SecurePass123");
|
||||
expect(result.isSuccess).toBe(true);
|
||||
|
||||
const isValid = await result.data.compare("WrongPassword");
|
||||
const isValid = await result.data.verifyPassword("WrongPassword");
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,35 @@
|
||||
import { Result, ValueObject } from "@common/domain";
|
||||
import bcrypt from "bcrypt";
|
||||
import { z } from "zod";
|
||||
|
||||
export class HashPassword extends ValueObject<string> {
|
||||
private static readonly SALT_ROUNDS = 10;
|
||||
|
||||
static create(plainPassword: string): Result<HashPassword, Error> {
|
||||
const result = HashPassword.validate(plainPassword);
|
||||
|
||||
if (!result.success) {
|
||||
return Result.fail(new Error(result.error.errors[0].message));
|
||||
}
|
||||
|
||||
const hashed = bcrypt.hashSync(result.data, this.SALT_ROUNDS);
|
||||
return Result.ok(new HashPassword(hashed));
|
||||
}
|
||||
|
||||
private static validate(password: string) {
|
||||
const schema = z.string().min(6, { message: "Password must be at least 6 characters long" });
|
||||
return schema.safeParse(password);
|
||||
}
|
||||
|
||||
static createFromHash(hashedPassword: string): Result<HashPassword, Error> {
|
||||
return Result.ok(new HashPassword(hashedPassword));
|
||||
}
|
||||
|
||||
static createFromPlainText(plainTextPassword: string): Result<HashPassword, Error> {
|
||||
return HashPassword.create(plainTextPassword);
|
||||
}
|
||||
|
||||
async verifyPassword(plainTextPassword: string): Promise<boolean> {
|
||||
return await bcrypt.compare(plainTextPassword, this._value);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./auth-user-roles";
|
||||
export * from "./email-address";
|
||||
export * from "./password-hash";
|
||||
export * from "./hash-password";
|
||||
export * from "./plain-password";
|
||||
export * from "./username";
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import { Result, ValueObject } from "@common/domain";
|
||||
import bcrypt from "bcrypt";
|
||||
import { z } from "zod";
|
||||
|
||||
export class PasswordHash extends ValueObject<string> {
|
||||
private static readonly SALT_ROUNDS = 10;
|
||||
|
||||
static create(plainPassword: string): Result<PasswordHash, Error> {
|
||||
const result = PasswordHash.validate(plainPassword);
|
||||
|
||||
if (!result.success) {
|
||||
return Result.fail(new Error(result.error.errors[0].message));
|
||||
}
|
||||
|
||||
const hashed = bcrypt.hashSync(result.data, this.SALT_ROUNDS);
|
||||
return Result.ok(new PasswordHash(hashed));
|
||||
}
|
||||
|
||||
private static validate(password: string) {
|
||||
const schema = z.string().min(6, { message: "Password must be at least 6 characters long" });
|
||||
return schema.safeParse(password);
|
||||
}
|
||||
|
||||
static fromHash(hash: string): PasswordHash {
|
||||
return new PasswordHash(hash);
|
||||
}
|
||||
|
||||
async compare(plainPassword: string): Promise<boolean> {
|
||||
return await bcrypt.compare(plainPassword, this._value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { Result, ValueObject } from "@common/domain";
|
||||
import { z } from "zod";
|
||||
|
||||
export class PlainPassword extends ValueObject<string> {
|
||||
static create(plainTextPassword: string): Result<PlainPassword, Error> {
|
||||
const result = PlainPassword.validate(plainTextPassword);
|
||||
|
||||
if (!result.success) {
|
||||
return Result.fail(new Error(result.error.errors[0].message));
|
||||
}
|
||||
|
||||
return Result.ok(new PlainPassword(result.data));
|
||||
}
|
||||
|
||||
private static validate(password: string) {
|
||||
const schema = z.string().min(6, { message: "Password must be at least 6 characters long" });
|
||||
return schema.safeParse(password);
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,8 @@
|
||||
import { Result } from "@common/domain";
|
||||
import { AuthenticatedUser } from "@contexts/auth/domain";
|
||||
import { AuthUserModel } from "../sequelize";
|
||||
|
||||
export interface IAuthenticatedUserMapper {
|
||||
/**
|
||||
* 🔹 Convierte una entidad de la base de datos en un agregado de dominio `AuthenticatedUser`
|
||||
*/
|
||||
toDomain(entity: any): Result<AuthenticatedUser, Error>;
|
||||
|
||||
/**
|
||||
* 🔹 Convierte un agregado `AuthenticatedUser` en un objeto listo para persistencia
|
||||
*/
|
||||
toPersistence(aggregate: AuthenticatedUser): any;
|
||||
toDomain(entity: AuthUserModel): Result<AuthenticatedUser, Error>;
|
||||
toPersistence(aggregate: AuthenticatedUser): AuthUserModel;
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { AuthenticatedUser, EmailAddress, PasswordHash, Username } from "@contexts/auth/domain";
|
||||
import { AuthenticatedUser, EmailAddress, HashPassword, Username } from "@contexts/auth/domain";
|
||||
import { AuthUserModel } from "../sequelize";
|
||||
import { IAuthenticatedUserMapper } from "./authenticated-user-mapper.interface";
|
||||
|
||||
export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
|
||||
/**
|
||||
* 🔹 Convierte una entidad de la base de datos en un agregado de dominio `AuthenticatedUser`
|
||||
*/
|
||||
toDomain(entity: any): Result<AuthenticatedUser, Error> {
|
||||
toDomain(entity: AuthUserModel): Result<AuthenticatedUser, Error> {
|
||||
if (!entity) {
|
||||
return Result.fail(new Error("Entity not found"));
|
||||
}
|
||||
@ -14,9 +15,9 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
|
||||
// Crear Value Objects asegurando que sean válidos
|
||||
const uniqueIdResult = UniqueID.create(entity.id);
|
||||
const usernameResult = Username.create(entity.username);
|
||||
const passwordHashResult = PasswordHash.create(entity.passwordHash);
|
||||
const passwordHashResult = HashPassword.createFromHash(entity.hash_password);
|
||||
const emailResult = EmailAddress.create(entity.email);
|
||||
1;
|
||||
|
||||
// Validar que no haya errores en la creación de los Value Objects
|
||||
const okOrError = Result.combine([
|
||||
uniqueIdResult,
|
||||
@ -33,9 +34,8 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
|
||||
{
|
||||
username: usernameResult.data!,
|
||||
email: emailResult.data!,
|
||||
passwordHash: passwordHashResult.data!,
|
||||
hashPassword: passwordHashResult.data!,
|
||||
roles: entity.roles || [],
|
||||
token: entity.token,
|
||||
},
|
||||
uniqueIdResult.data!
|
||||
);
|
||||
@ -44,7 +44,7 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
|
||||
/**
|
||||
* 🔹 Convierte un agregado `AuthenticatedUser` en un objeto listo para persistencia
|
||||
*/
|
||||
toPersistence(authenticatedUser: AuthenticatedUser): any {
|
||||
toPersistence(authenticatedUser: AuthenticatedUser): AuthUserModel {
|
||||
return authenticatedUser.toPersistenceData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
export * from "./authenticated-user-mapper.interface";
|
||||
export * from "./authenticated-user.mapper";
|
||||
export * from "./tab-context-mapper.interface";
|
||||
export * from "./tab-context.mapper";
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { Result } from "@common/domain";
|
||||
import { TabContext } from "@contexts/auth/domain";
|
||||
import { TabContextModel } from "../sequelize";
|
||||
|
||||
export interface ITabContextMapper {
|
||||
toDomain(entity: TabContextModel): Result<TabContext, Error>;
|
||||
toPersistence(aggregate: TabContext): TabContextModel;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { TabContext } from "@contexts/auth/domain";
|
||||
import { TabContextModel } from "../sequelize";
|
||||
import { ITabContextMapper } from "./tab-context-mapper.interface";
|
||||
|
||||
export class TabContextMapper implements ITabContextMapper {
|
||||
toDomain(entity: TabContextModel): Result<TabContext, Error> {
|
||||
if (!entity) {
|
||||
return Result.fail(new Error("Entity not found"));
|
||||
}
|
||||
|
||||
// Crear Value Objects asegurando que sean válidos
|
||||
const uniqueIdResult = UniqueID.create(entity.id);
|
||||
const tabIdResult = UniqueID.create(entity.tab_id);
|
||||
const userIdResult = UniqueID.create(entity.user_id);
|
||||
const companyIdResult = UniqueID.create(entity.company_id, false);
|
||||
const brachIdResult = UniqueID.create(entity.branch_id, false);
|
||||
|
||||
// Validar que no haya errores en la creación de los Value Objects
|
||||
const okOrError = Result.combine([
|
||||
uniqueIdResult,
|
||||
tabIdResult,
|
||||
userIdResult,
|
||||
companyIdResult,
|
||||
brachIdResult,
|
||||
]);
|
||||
if (okOrError.isFailure) {
|
||||
return Result.fail(okOrError.error.message);
|
||||
}
|
||||
|
||||
// Crear el agregado de dominio
|
||||
return TabContext.create(
|
||||
{
|
||||
tabId: tabIdResult.data!,
|
||||
userId: userIdResult.data!,
|
||||
companyId: companyIdResult.data,
|
||||
branchId: brachIdResult.data,
|
||||
},
|
||||
uniqueIdResult.data!
|
||||
);
|
||||
}
|
||||
|
||||
toPersistence(tabContext: TabContext): TabContextModel {
|
||||
return tabContext.toPersistenceData();
|
||||
}
|
||||
}
|
||||
|
||||
export const createTabContextMapper = (): ITabContextMapper => new TabContextMapper();
|
||||
@ -5,6 +5,7 @@ import {
|
||||
AuthenticatedUser,
|
||||
EmailAddress,
|
||||
IAuthenticatedUserRepository,
|
||||
PlainPassword,
|
||||
} from "@contexts/auth/domain";
|
||||
import jwt from "jsonwebtoken";
|
||||
import passport from "passport";
|
||||
@ -20,6 +21,22 @@ export class PassportAuthProvider implements IAuthProvider {
|
||||
private readonly _repository: IAuthenticatedUserRepository;
|
||||
private readonly _transactionManager!: ITransactionManager;
|
||||
|
||||
private async _verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> {
|
||||
const emailVO = EmailAddress.create(email);
|
||||
if (emailVO.isFailure) return Promise.resolve(null);
|
||||
|
||||
const passwordVO = PlainPassword.create(password);
|
||||
if (passwordVO.isFailure) return Promise.resolve(null);
|
||||
|
||||
const userResult = await this._repository.getUserByEmail(emailVO.data);
|
||||
if (userResult.isFailure || !userResult.data) return Promise.resolve(null);
|
||||
|
||||
const user = userResult.data;
|
||||
const isValidPassword = await user.verifyPassword(passwordVO.data);
|
||||
|
||||
return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔹 Configura PassportJS
|
||||
*/
|
||||
@ -31,10 +48,9 @@ export class PassportAuthProvider implements IAuthProvider {
|
||||
|
||||
passport.use(
|
||||
"jwt",
|
||||
new JwtStrategy(jwtOptions, (payload, done) => {
|
||||
new JwtStrategy(jwtOptions, (tokenPayload, done) => {
|
||||
try {
|
||||
console.log(payload);
|
||||
return done(null, payload);
|
||||
return done(null, tokenPayload);
|
||||
} catch (error) {
|
||||
return done(error, false);
|
||||
}
|
||||
@ -47,7 +63,7 @@ export class PassportAuthProvider implements IAuthProvider {
|
||||
{ usernameField: "email", passwordField: "password" },
|
||||
async (email, password, done) => {
|
||||
try {
|
||||
const user = await this.verifyUser(email, password);
|
||||
const user = await this._verifyUser(email, password);
|
||||
return user
|
||||
? done(null, user)
|
||||
: done(null, false, { message: "Invalid email or password" });
|
||||
@ -67,30 +83,17 @@ export class PassportAuthProvider implements IAuthProvider {
|
||||
this.initializePassport();
|
||||
}
|
||||
|
||||
generateAccessToken(payload: any): string {
|
||||
return jwt.sign(payload, SECRET_KEY, { expiresIn: ACCESS_EXPIRATION });
|
||||
generateAccessToken(payload: object): string {
|
||||
return jwt.sign(payload, SECRET_KEY, { expiresIn: String(ACCESS_EXPIRATION) });
|
||||
}
|
||||
|
||||
generateRefreshToken(payload: any): string {
|
||||
generateRefreshToken(payload: object): string {
|
||||
return jwt.sign(payload, SECRET_KEY, { expiresIn: REFRESH_EXPIRATION });
|
||||
}
|
||||
|
||||
verifyToken(token: string): any {
|
||||
return jwt.verify(token, SECRET_KEY);
|
||||
}
|
||||
|
||||
async verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> {
|
||||
const emailVO = EmailAddress.create(email);
|
||||
if (emailVO.isFailure) return Promise.resolve(null);
|
||||
|
||||
const userResult = await this._repository.findUserByEmail(emailVO.data);
|
||||
if (userResult.isFailure || !userResult.data) return Promise.resolve(null);
|
||||
|
||||
const user = userResult.data;
|
||||
const isValidPassword = await user.comparePassword(password);
|
||||
|
||||
return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user);
|
||||
}
|
||||
}
|
||||
|
||||
export const createPassportAuthProvider = (
|
||||
@ -101,3 +104,5 @@ export const createPassportAuthProvider = (
|
||||
const _repository = repository || createAuthenticatedUserRepository();
|
||||
return new PassportAuthProvider(_repository, _transactionManager);
|
||||
};
|
||||
|
||||
export const initializePassportAuthProvide = () => createPassportAuthProvider();
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
import { DataTypes, InferAttributes, InferCreationAttributes, Model, Sequelize } from "sequelize";
|
||||
import {
|
||||
DataTypes,
|
||||
InferAttributes,
|
||||
InferCreationAttributes,
|
||||
Model,
|
||||
NonAttribute,
|
||||
Sequelize,
|
||||
} from "sequelize";
|
||||
import { TabContextCreationAttributes, TabContextModel } from "./tab-context.model";
|
||||
|
||||
export type AuthUserCreationAttributes = InferCreationAttributes<AuthUserModel>;
|
||||
export type AuthUserCreationAttributes = InferCreationAttributes<
|
||||
AuthUserModel,
|
||||
{ omit: "contexts" }
|
||||
> & {
|
||||
contexts: TabContextCreationAttributes[];
|
||||
};
|
||||
|
||||
export class AuthUserModel extends Model<
|
||||
InferAttributes<AuthUserModel>,
|
||||
@ -11,13 +24,22 @@ export class AuthUserModel extends Model<
|
||||
return Promise.resolve();
|
||||
}*/
|
||||
|
||||
static associate(connection: Sequelize) {}
|
||||
static associate(connection: Sequelize) {
|
||||
const { TabContextModel } = connection.models;
|
||||
|
||||
AuthUserModel.hasMany(TabContextModel, {
|
||||
as: "contexts",
|
||||
foreignKey: "user_id",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
}
|
||||
declare id: string;
|
||||
declare username: string;
|
||||
declare email: string;
|
||||
declare password: string;
|
||||
declare hash_password: string;
|
||||
declare roles: string[];
|
||||
declare isActive: boolean;
|
||||
|
||||
declare contexts: NonAttribute<TabContextModel[]>;
|
||||
}
|
||||
|
||||
export default (sequelize: Sequelize) => {
|
||||
@ -35,7 +57,7 @@ export default (sequelize: Sequelize) => {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
password: {
|
||||
hash_password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
@ -52,10 +74,6 @@ export default (sequelize: Sequelize) => {
|
||||
this.setDataValue("roles", rawValue);
|
||||
},
|
||||
},
|
||||
isActive: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
@ -68,6 +86,12 @@ export default (sequelize: Sequelize) => {
|
||||
deletedAt: "deleted_at",
|
||||
|
||||
indexes: [{ name: "email_idx", fields: ["email"], unique: true }],
|
||||
|
||||
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||
|
||||
defaultScope: {},
|
||||
|
||||
scopes: {},
|
||||
}
|
||||
);
|
||||
return AuthUserModel;
|
||||
|
||||
@ -49,20 +49,21 @@ export class AuthenticatedUserRepository
|
||||
}
|
||||
}
|
||||
|
||||
async findUserByEmail(
|
||||
async getUserByEmail(
|
||||
email: EmailAddress,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<AuthenticatedUser | null, Error>> {
|
||||
): Promise<Result<AuthenticatedUser, Error>> {
|
||||
try {
|
||||
const rawUser: any = await this._findById(
|
||||
const rawUser: any = await this._getBy(
|
||||
AuthUserModel,
|
||||
"email",
|
||||
email.toString(),
|
||||
{},
|
||||
transaction
|
||||
);
|
||||
|
||||
if (!rawUser === true) {
|
||||
return Result.ok(null);
|
||||
return Result.fail(new Error("User with email not exists"));
|
||||
}
|
||||
|
||||
return this._mapper.toDomain(rawUser);
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
export * from "./auth-user.model";
|
||||
export * from "./authenticated-user.repository";
|
||||
export * from "./tab-context.model";
|
||||
export * from "./tab-context.repository";
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
import {
|
||||
DataTypes,
|
||||
InferAttributes,
|
||||
InferCreationAttributes,
|
||||
Model,
|
||||
NonAttribute,
|
||||
Sequelize,
|
||||
} from "sequelize";
|
||||
import { AuthUserModel } from "./auth-user.model";
|
||||
|
||||
export type TabContextCreationAttributes = InferCreationAttributes<
|
||||
TabContextModel,
|
||||
{ omit: "user" }
|
||||
>;
|
||||
|
||||
export class TabContextModel extends Model<
|
||||
InferAttributes<TabContextModel, { omit: "user" }>,
|
||||
InferCreationAttributes<TabContextModel, { omit: "user" }>
|
||||
> {
|
||||
// To avoid table creation
|
||||
/*static async sync(): Promise<any> {
|
||||
return Promise.resolve();
|
||||
}*/
|
||||
static associate(connection: Sequelize) {
|
||||
const { AuthUserModel } = connection.models;
|
||||
|
||||
TabContextModel.belongsTo(AuthUserModel, {
|
||||
as: "user",
|
||||
foreignKey: "user_id",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
}
|
||||
|
||||
declare id: string;
|
||||
declare tab_id: string;
|
||||
declare user_id: string;
|
||||
declare company_id: string;
|
||||
declare branch_id: string;
|
||||
|
||||
declare user: NonAttribute<AuthUserModel>;
|
||||
}
|
||||
|
||||
export default (sequelize: Sequelize) => {
|
||||
TabContextModel.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
},
|
||||
user_id: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
tab_id: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
company_id: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
branch_id: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: "user_tab_contexts",
|
||||
paranoid: true, // softs deletes
|
||||
timestamps: true,
|
||||
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
deletedAt: "deleted_at",
|
||||
|
||||
indexes: [{ name: "tab_id_idx", fields: ["tab_id"], unique: true }],
|
||||
|
||||
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||
|
||||
defaultScope: {},
|
||||
|
||||
scopes: {},
|
||||
}
|
||||
);
|
||||
return TabContextModel;
|
||||
};
|
||||
@ -0,0 +1,132 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { SequelizeRepository } from "@common/infrastructure";
|
||||
import { TabContext } from "@contexts/auth/domain/";
|
||||
import { ITabContextRepository } from "@contexts/auth/domain/repositories/tab-context-repository.interface";
|
||||
import { Transaction } from "sequelize";
|
||||
import { createTabContextMapper, ITabContextMapper } from "../mappers";
|
||||
import { TabContextModel } from "./tab-context.model";
|
||||
|
||||
export class TabContextRepository
|
||||
extends SequelizeRepository<TabContext>
|
||||
implements ITabContextRepository
|
||||
{
|
||||
private readonly _mapper!: ITabContextMapper;
|
||||
|
||||
/**
|
||||
* 🔹 Función personalizada para mapear errores de unicidad en autenticación
|
||||
*/
|
||||
private _customErrorMapper(error: Error): string | null {
|
||||
if (error.name === "SequelizeUniqueConstraintError") {
|
||||
return "Tab context already exists";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(mapper: ITabContextMapper) {
|
||||
super();
|
||||
this._mapper = mapper;
|
||||
}
|
||||
|
||||
async getContextByTabId(
|
||||
tabId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<TabContext, Error>> {
|
||||
try {
|
||||
const rawContext = await this._getBy(
|
||||
TabContextModel,
|
||||
"tab_id",
|
||||
tabId.toString(),
|
||||
{},
|
||||
transaction
|
||||
);
|
||||
|
||||
if (!rawContext === true) {
|
||||
return Result.fail(new Error("Tab context not exists"));
|
||||
}
|
||||
|
||||
return this._mapper.toDomain(rawContext);
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
|
||||
async contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> {
|
||||
try {
|
||||
const result: any = await this._exists(
|
||||
TabContextModel,
|
||||
"tab_id",
|
||||
tabId.toString(),
|
||||
transaction
|
||||
);
|
||||
|
||||
return Result.ok(Boolean(result));
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea un contexto para un tab id o actualiza si ya existe
|
||||
* @param context
|
||||
* @param transaction
|
||||
* @returns
|
||||
*/
|
||||
|
||||
async registerContext(
|
||||
context: TabContext,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<void, Error>> {
|
||||
try {
|
||||
const { id } = context;
|
||||
const persistenceData = this._mapper.toPersistence(context);
|
||||
|
||||
await this._save(TabContextModel, id, persistenceData, {}, transaction);
|
||||
return Result.ok();
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
|
||||
async updateCompanyByTabId(
|
||||
tabId: UniqueID,
|
||||
companyId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<void, Error>> {
|
||||
try {
|
||||
await TabContextModel.update(
|
||||
{ company_id: companyId.toString() },
|
||||
{
|
||||
where: {
|
||||
tab_id: tabId.toString(),
|
||||
},
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
|
||||
return Result.ok();
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>> {
|
||||
try {
|
||||
await TabContextModel.destroy({
|
||||
where: {
|
||||
tab_id: tabId.toString(),
|
||||
},
|
||||
transaction,
|
||||
force: false,
|
||||
});
|
||||
return Result.ok();
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const createTabContextRepository = (): ITabContextRepository => {
|
||||
const tabContextMapper = createTabContextMapper();
|
||||
return new TabContextRepository(tabContextMapper);
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import { Result, UniqueID } from "@common/domain";
|
||||
import { ExpressController } from "@common/presentation";
|
||||
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
||||
import { EmailAddress, PasswordHash } from "@contexts/auth/domain";
|
||||
import { EmailAddress, PlainPassword } from "@contexts/auth/domain";
|
||||
import { ILoginPresenter, LoginPresenter } from "./login.presenter";
|
||||
|
||||
class LoginController extends ExpressController {
|
||||
@ -14,16 +15,21 @@ class LoginController extends ExpressController {
|
||||
}
|
||||
|
||||
async executeImpl() {
|
||||
const tabId = this.req.headers["x-tab-id"];
|
||||
const emailVO = EmailAddress.create(this.req.body.email);
|
||||
const passwordHashVO = PasswordHash.create(this.req.body.password);
|
||||
const plainPasswordVO = PlainPassword.create(this.req.body.password);
|
||||
const tabIdVO = UniqueID.create(String(tabId));
|
||||
|
||||
if ([emailVO, passwordHashVO].some((r) => r.isFailure)) {
|
||||
return this.clientError("Invalid input data");
|
||||
const resultValidation = Result.combine([emailVO, plainPasswordVO, tabIdVO]);
|
||||
|
||||
if (resultValidation.isFailure) {
|
||||
return this.clientError("Invalid input data", resultValidation.error);
|
||||
}
|
||||
|
||||
const userOrError = await this._authService.loginUser({
|
||||
email: emailVO.data,
|
||||
passwordHash: passwordHashVO.data,
|
||||
plainPassword: plainPasswordVO.data,
|
||||
tabId: tabIdVO.data,
|
||||
});
|
||||
|
||||
if (userOrError.isFailure) {
|
||||
|
||||
@ -2,20 +2,40 @@ import { AuthenticatedUser } from "@contexts/auth/domain";
|
||||
import { ILoginUserResponseDTO } from "../../dto";
|
||||
|
||||
export interface ILoginPresenter {
|
||||
map: (user: AuthenticatedUser) => ILoginUserResponseDTO;
|
||||
map: (data: {
|
||||
user: AuthenticatedUser;
|
||||
tokens: {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
}) => ILoginUserResponseDTO;
|
||||
}
|
||||
|
||||
export const LoginPresenter: ILoginPresenter = {
|
||||
map: (user: AuthenticatedUser): ILoginUserResponseDTO => {
|
||||
//const { user, token, refreshToken } = loginUser;
|
||||
//const roles = user.getRoles()?.map((rol) => rol.toString()) || [];
|
||||
map: (data: {
|
||||
user: AuthenticatedUser;
|
||||
tokens: {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
}): ILoginUserResponseDTO => {
|
||||
const {
|
||||
user,
|
||||
tokens: { accessToken, refreshToken },
|
||||
} = data;
|
||||
|
||||
const userData = user.toPersistenceData();
|
||||
|
||||
return {
|
||||
user_id: userData,
|
||||
access_token: userData.accessToken,
|
||||
refresh_token: userData.refreshToken,
|
||||
user: {
|
||||
id: userData.id,
|
||||
email: userData.email,
|
||||
username: userData.username,
|
||||
},
|
||||
tokens: {
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from "./logout.controller";
|
||||
@ -0,0 +1,38 @@
|
||||
import { UniqueID } from "@common/domain";
|
||||
import { ExpressController } from "@common/presentation";
|
||||
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
||||
|
||||
class LogoutController extends ExpressController {
|
||||
private readonly _authService!: IAuthService;
|
||||
|
||||
public constructor(authService: IAuthService) {
|
||||
super();
|
||||
this._authService = authService;
|
||||
}
|
||||
|
||||
async executeImpl() {
|
||||
const tabId = this.req.headers["x-tab-id"];
|
||||
const tabIdVO = UniqueID.create(String(tabId));
|
||||
|
||||
if (tabIdVO.isFailure) {
|
||||
return this.clientError("Invalid tab id", [tabIdVO.error]);
|
||||
}
|
||||
|
||||
const userOrError = await this._authService.logoutUser({
|
||||
email: emailVO.data,
|
||||
plainPassword: plainPasswordVO.data,
|
||||
tabId: tabIdVO.data,
|
||||
});
|
||||
|
||||
if (userOrError.isFailure) {
|
||||
return this.unauthorizedError(userOrError.error.message);
|
||||
}
|
||||
|
||||
return this.ok();
|
||||
}
|
||||
}
|
||||
|
||||
export const createLogoutController = () => {
|
||||
const authService = createAuthService();
|
||||
return new LogoutController(authService);
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
import { ExpressController } from "@common/presentation";
|
||||
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
||||
import { EmailAddress, PasswordHash, Username } from "@contexts/auth/domain";
|
||||
import { EmailAddress, HashPassword, Username } from "@contexts/auth/domain";
|
||||
import { IRegisterPresenter, RegisterPresenter } from "./register.presenter";
|
||||
|
||||
class RegisterController extends ExpressController {
|
||||
@ -16,16 +16,16 @@ class RegisterController extends ExpressController {
|
||||
async executeImpl() {
|
||||
const emailVO = EmailAddress.create(this.req.body.email);
|
||||
const usernameVO = Username.create(this.req.body.username);
|
||||
const passwordHashVO = PasswordHash.create(this.req.body.password);
|
||||
const hashPasswordVO = HashPassword.create(this.req.body.password);
|
||||
|
||||
if ([emailVO, usernameVO, passwordHashVO].some((r) => r.isFailure)) {
|
||||
if ([emailVO, usernameVO, hashPasswordVO].some((r) => r.isFailure)) {
|
||||
return this.clientError("Invalid input data");
|
||||
}
|
||||
|
||||
const userOrError = await this._authService.registerUser({
|
||||
username: usernameVO.data,
|
||||
email: emailVO.data,
|
||||
passwordHash: passwordHashVO.data,
|
||||
hashPassword: hashPasswordVO.data,
|
||||
});
|
||||
|
||||
if (userOrError.isFailure) {
|
||||
|
||||
@ -5,9 +5,16 @@ export interface IRegisterUserResponseDTO {
|
||||
}
|
||||
|
||||
export interface ILoginUserResponseDTO {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
user_id: string;
|
||||
user: {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
};
|
||||
tokens: {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
};
|
||||
//tab_id: string;
|
||||
}
|
||||
|
||||
export interface ILogoutResponseDTO {
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from "./passport-auth.middleware";
|
||||
export * from "./tab-context.middleware";
|
||||
|
||||
@ -10,10 +10,10 @@ interface AuthenticatedRequest extends Request {
|
||||
}
|
||||
|
||||
// Middleware para autenticar usando passport con el local-jwt strategy
|
||||
const authenticateJwt = passport.authenticate("jwt", { session: false });
|
||||
const _authenticateJwt = passport.authenticate("jwt", { session: false });
|
||||
|
||||
// Comprueba el rol del usuario
|
||||
const authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => {
|
||||
const _authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => {
|
||||
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||
const user = req.user as AuthenticatedUser;
|
||||
if (!user || !condition(user)) {
|
||||
@ -33,14 +33,14 @@ const authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => {
|
||||
};
|
||||
|
||||
// Verifica que el usuario esté autenticado
|
||||
export const checkUser = [authenticateJwt, authorizeUser((user) => user.isUser)];
|
||||
export const validateUser = [_authenticateJwt, _authorizeUser((user) => user.isUser)];
|
||||
|
||||
// Verifica que el usuario sea administrador
|
||||
export const checkIsAdmin = [authenticateJwt, authorizeUser((user) => user.isAdmin)];
|
||||
export const validateUserIsAdmin = [_authenticateJwt, _authorizeUser((user) => user.isAdmin)];
|
||||
|
||||
// Middleware para verificar que el usuario sea administrador o el dueño de los datos (self)
|
||||
export const checkAdminOrSelf = [
|
||||
authenticateJwt,
|
||||
export const validateUserIsAdminOrOwner = [
|
||||
_authenticateJwt,
|
||||
(req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||
const user = req.user as AuthenticatedUser;
|
||||
const { userId } = req.params;
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import { UniqueID } from "@common/domain";
|
||||
import { ApiError, ExpressController } from "@common/presentation";
|
||||
import { createTabContextService } from "@contexts/auth/application";
|
||||
import { TabContext } from "@contexts/auth/domain";
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import httpStatus from "http-status";
|
||||
|
||||
// Extender el Request de Express para incluir el usuario autenticado optionalmente
|
||||
interface TabContextRequest extends Request {
|
||||
tabContext?: TabContext;
|
||||
}
|
||||
|
||||
export const validateTabContextHeader = async (
|
||||
req: TabContextRequest,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const tabId = String(req.headers["x-tab-id"]);
|
||||
if (!tabId) {
|
||||
return ExpressController.errorResponse(
|
||||
new ApiError({
|
||||
status: 401,
|
||||
title: httpStatus["401"],
|
||||
name: httpStatus["401_NAME"],
|
||||
detail: "Tab ID is required",
|
||||
}),
|
||||
res
|
||||
);
|
||||
}
|
||||
|
||||
const tabIdOrError = UniqueID.create(tabId, false);
|
||||
if (tabIdOrError.isFailure) {
|
||||
return ExpressController.errorResponse(
|
||||
new ApiError({
|
||||
status: 422,
|
||||
title: httpStatus["422"],
|
||||
name: httpStatus["422_NAME"],
|
||||
detail: "Invalid Tab ID",
|
||||
}),
|
||||
res
|
||||
);
|
||||
}
|
||||
const contextOrError = await createTabContextService().getContextByTabId(tabIdOrError.data);
|
||||
if (contextOrError.isFailure) {
|
||||
return ExpressController.errorResponse(
|
||||
new ApiError({
|
||||
status: 401,
|
||||
title: httpStatus["401"],
|
||||
name: httpStatus["401_NAME"],
|
||||
detail: "Invalid or expired Tab ID",
|
||||
}),
|
||||
res
|
||||
);
|
||||
}
|
||||
|
||||
const context = contextOrError.data;
|
||||
|
||||
req.tabContext = context;
|
||||
next();
|
||||
};
|
||||
@ -1,8 +1,7 @@
|
||||
import { validateRequest } from "@common/presentation";
|
||||
import {
|
||||
createLoginController,
|
||||
createRegisterController,
|
||||
} from "@contexts/auth/presentation/controllers";
|
||||
import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation";
|
||||
import { createLoginController } from "@contexts/auth/presentation/controllers";
|
||||
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
|
||||
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
|
||||
import { Router } from "express";
|
||||
|
||||
@ -33,6 +32,7 @@ export const authRouter = (appRouter: Router) => {
|
||||
* @apiGroup Authentication
|
||||
* @apiVersion 1.0.0
|
||||
*
|
||||
* @apiHeader {String} Tab ID (x-tab-id)
|
||||
* @apiBody {String} email User's email address.
|
||||
* @apiBody {String} password User's password.
|
||||
*
|
||||
@ -41,25 +41,14 @@ export const authRouter = (appRouter: Router) => {
|
||||
*
|
||||
* @apiError (401) {String} message Invalid email or password.
|
||||
*/
|
||||
authRoutes.post("/login", validateRequest(LoginUserSchema), (req, res, next) => {
|
||||
createLoginController().execute(req, res, next);
|
||||
});
|
||||
|
||||
/**
|
||||
* @api {post} /api/auth/select-company Select an active company
|
||||
* @apiName SelectCompany
|
||||
* @apiGroup Authentication
|
||||
* @apiVersion 1.0.0
|
||||
*
|
||||
* @apiHeader {String} Authorization Bearer token.
|
||||
*
|
||||
* @apiBody {String} companyId The ID of the company to select.
|
||||
*
|
||||
* @apiSuccess (200) {String} message Success message.
|
||||
*
|
||||
* @apiError (403) {String} message Unauthorized or invalid company selection.
|
||||
*/
|
||||
//authRoutes.post("/select-company", authMiddleware, authController.selectCompany);
|
||||
authRoutes.post(
|
||||
"/login",
|
||||
validateRequest(LoginUserSchema),
|
||||
validateTabContextHeader,
|
||||
(req, res, next) => {
|
||||
createLoginController().execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @api {post} /api/auth/logout Logout user
|
||||
@ -67,11 +56,15 @@ export const authRouter = (appRouter: Router) => {
|
||||
* @apiGroup Authentication
|
||||
* @apiVersion 1.0.0
|
||||
*
|
||||
* @apiHeader {String} Tab ID (x-tab-id)
|
||||
* @apiHeader {String} Authorization Bearer token.
|
||||
*
|
||||
* @apiSuccess (200) {String} message Success message.
|
||||
*/
|
||||
//authRoutes.post("/logout", authMiddleware, authController.logout);
|
||||
authRoutes.post("/logout", validateUser, validateTabContextHeader, (req, res, next) => {
|
||||
res.sendStatus(200);
|
||||
//createLogoutController().execute(req, res, next);
|
||||
});
|
||||
|
||||
appRouter.use("/auth", authRoutes);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user