This commit is contained in:
David Arranz 2025-02-05 21:40:59 +01:00
parent 8ead5a62da
commit 36788f34e8
34 changed files with 258 additions and 2122 deletions

View File

@ -6,14 +6,9 @@
"auditLog": ".571b27c4f3dcc376d1f0ca8880ce87cfefd2f30d-audit.json", "auditLog": ".571b27c4f3dcc376d1f0ca8880ce87cfefd2f30d-audit.json",
"files": [ "files": [
{ {
"date": 1738578708264, "date": 1738768744297,
"name": "debug-2025-02-03.log", "name": "debug-2025-02-05.log",
"hash": "48ca17f819e391cb5ae1909a6ee0fa4d9c8cdb6667ef893547acb004f4a79737" "hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256"
},
{
"date": 1738664864746,
"name": "debug-2025-02-04.log",
"hash": "7f1ecce0e9a97fbb99865ac9bfc3591897975a2fd9562164c9be52aabc47f47f"
} }
], ],
"hashType": "sha256" "hashType": "sha256"

View File

@ -6,14 +6,9 @@
"auditLog": ".e6616b1c93d5e50d48b909cd34375b545b447bc6-audit.json", "auditLog": ".e6616b1c93d5e50d48b909cd34375b545b447bc6-audit.json",
"files": [ "files": [
{ {
"date": 1738578708262, "date": 1738768744292,
"name": "error-2025-02-03.log", "name": "error-2025-02-05.log",
"hash": "a21f154f5c386a75eee98a35c2b100da7df1b8002cf99851b90bd12810f1fe8a" "hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8"
},
{
"date": 1738664864743,
"name": "error-2025-02-04.log",
"hash": "dfb19c1e5b9c2039572425939e77f4d4ab3285df0fcded1edfba3e7c4cc2a94d"
} }
], ],
"hashType": "sha256" "hashType": "sha256"

File diff suppressed because it is too large Load Diff

View File

@ -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"}

View File

@ -1,5 +1,6 @@
import { logger } from "@common/infrastructure/logger"; import { logger } from "@common/infrastructure/logger";
import { globalErrorHandler } from "@common/presentation"; import { globalErrorHandler } from "@common/presentation";
import { initializePassportAuthProvide } from "@contexts/auth/infraestructure";
import dotenv from "dotenv"; import dotenv from "dotenv";
import express, { Application } from "express"; import express, { Application } from "express";
import helmet from "helmet"; import helmet from "helmet";
@ -24,7 +25,10 @@ export function createApp(): Application {
app.use(responseTime()); // set up the response-time middleware app.use(responseTime()); // set up the response-time middleware
// Inicializar Passport // Inicializar Passport
//app.use((req, res, next) => createPassportAuthProvider()); app.use((req, res, next) => {
initializePassportAuthProvide();
next();
});
app.use((req, _, next) => { app.use((req, _, next) => {
logger.info(`▶️ Incoming request ${req.method} to ${req.path}`); logger.info(`▶️ Incoming request ${req.method} to ${req.path}`);

View File

@ -109,8 +109,9 @@ export abstract class ExpressController {
return ExpressController.errorResponse( return ExpressController.errorResponse(
new ApiError({ new ApiError({
status: 422, status: 422,
title: "Invalid Input", title: httpStatus["422"],
detail: message, name: httpStatus["422_NAME"],
detail: message ?? httpStatus["422_MESSAGE"],
errors, errors,
}), }),
this.res this.res

View File

@ -2,10 +2,10 @@ import { AuthenticatedUser } from "../domain";
export interface IAuthProvider { export interface IAuthProvider {
/* JWT Strategy */ /* JWT Strategy */
generateAccessToken(payload: any): string; generateAccessToken(payload: object): string;
generateRefreshToken(payload: any): string; generateRefreshToken(payload: object): string;
verifyToken(token: string): any; verifyToken(token: string): Promise<AuthenticatedUser | null>;
/* LocalStrategy */ /* LocalStrategy */
verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>; //_verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>;
} }

View File

@ -1,14 +1,18 @@
import { Result, UniqueID } from "@common/domain"; import { Result, UniqueID } from "@common/domain";
import { AuthenticatedUser, EmailAddress, PasswordHash, Username } from "../domain"; import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain";
export interface IAuthService { export interface IAuthService {
registerUser(params: { registerUser(params: {
username: Username; username: Username;
email: EmailAddress; email: EmailAddress;
passwordHash: PasswordHash; hashPassword: HashPassword;
}): Promise<Result<AuthenticatedUser, Error>>; }): Promise<Result<AuthenticatedUser, Error>>;
loginUser(params: { email: EmailAddress; passwordHash: PasswordHash; tabId: UniqueID }): Promise< loginUser(params: {
email: EmailAddress;
plainPassword: PlainPassword;
tabId: UniqueID;
}): Promise<
Result< Result<
{ {
user: AuthenticatedUser; user: AuthenticatedUser;

View File

@ -3,8 +3,8 @@ import { ITransactionManager } from "@common/infrastructure/database";
import { import {
AuthenticatedUser, AuthenticatedUser,
EmailAddress, EmailAddress,
HashPassword,
IAuthenticatedUserRepository, IAuthenticatedUserRepository,
PasswordHash,
TabContext, TabContext,
Username, Username,
} from "../domain"; } from "../domain";
@ -37,29 +37,25 @@ export class AuthService implements IAuthService {
async registerUser(params: { async registerUser(params: {
username: Username; username: Username;
email: EmailAddress; email: EmailAddress;
passwordHash: PasswordHash; hashPassword: HashPassword;
}): Promise<Result<AuthenticatedUser, Error>> { }): Promise<Result<AuthenticatedUser, Error>> {
try { try {
return await this._transactionManager.complete(async (transaction) => { return await this._transactionManager.complete(async (transaction) => {
const { username, email, passwordHash } = params; const { username, email, hashPassword } = params;
// Verificar si el usuario ya existe // Verificar si el usuario ya existe
const userExists = await this._userRepo.findUserByEmail(email, transaction); const userExists = await this._userRepo.userExists(email, transaction);
if (userExists.isSuccess && userExists.data) { if (userExists.isSuccess && userExists.data) {
return Result.fail(new Error("Email is already registered")); return Result.fail(new Error("Email is already registered"));
} }
if (userExists.isFailure) {
return Result.fail(userExists.error);
}
const newUserId = UniqueID.generateNewID().data; const newUserId = UniqueID.generateNewID().data;
const userOrError = AuthenticatedUser.create( const userOrError = AuthenticatedUser.create(
{ {
username, username,
email, email,
passwordHash, hashPassword,
roles: ["USER"], roles: ["USER"],
}, },
newUserId newUserId
@ -88,7 +84,7 @@ export class AuthService implements IAuthService {
*/ */
async loginUser(params: { async loginUser(params: {
email: EmailAddress; email: EmailAddress;
passwordHash: PasswordHash; plainPassword: HashPassword;
tabId: UniqueID; tabId: UniqueID;
}): Promise< }): Promise<
Result< Result<
@ -104,7 +100,7 @@ export class AuthService implements IAuthService {
> { > {
try { try {
return await this._transactionManager.complete(async (transaction) => { return await this._transactionManager.complete(async (transaction) => {
const { email, passwordHash, tabId } = params; const { email, plainPassword, tabId } = params;
// Verificar que el tab ID está definido // Verificar que el tab ID está definido
if (!tabId.isDefined()) { if (!tabId.isDefined()) {
@ -112,7 +108,7 @@ export class AuthService implements IAuthService {
} }
// 🔹 Verificar si el usuario existe en la base de datos // 🔹 Verificar si el usuario existe en la base de datos
const userResult = await this._userRepo.findUserByEmail(email, transaction); const userResult = await this._userRepo.getUserByEmail(email, transaction);
if (userResult.isFailure) { if (userResult.isFailure) {
return Result.fail(new Error("Invalid email or password")); return Result.fail(new Error("Invalid email or password"));
} }
@ -120,7 +116,7 @@ export class AuthService implements IAuthService {
const user = userResult.data; const user = userResult.data;
// 🔹 Verificar que la contraseña sea correcta // 🔹 Verificar que la contraseña sea correcta
const isValidPassword = await user.comparePassword(passwordHash); const isValidPassword = await user.verifyPassword(plainPassword);
if (!isValidPassword) { if (!isValidPassword) {
return Result.fail(new Error("Invalid email or password")); return Result.fail(new Error("Invalid email or password"));
} }

View File

@ -5,6 +5,8 @@ import { createPassportAuthProvider } from "../infraestructure/passport/passport
import { IAuthProvider } from "./auth-provider.interface"; import { IAuthProvider } from "./auth-provider.interface";
import { IAuthService } from "./auth-service.interface"; import { IAuthService } from "./auth-service.interface";
import { AuthService } from "./auth.service"; 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-provider.interface";
export * from "./auth-service.interface"; export * from "./auth-service.interface";
@ -25,3 +27,10 @@ export const createAuthService = (): IAuthService => {
authProvider authProvider
); );
}; };
export const createTabContextService = (): ITabContextService => {
const transactionManager = createSequelizeTransactionManager();
const tabContextRepository = createTabContextRepository();
return new TabContextService(tabContextRepository, transactionManager);
};

View File

@ -2,7 +2,7 @@ import { Result, UniqueID } from "@common/domain";
import { TabContext } from "../domain"; import { TabContext } from "../domain";
export interface ITabContextService { export interface ITabContextService {
getByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>; getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>;
createContext(params: { createContext(params: {
tabId: UniqueID; tabId: UniqueID;
userId: UniqueID; userId: UniqueID;

View File

@ -16,7 +16,7 @@ export class TabContextService implements ITabContextService {
/** /**
* Obtiene el contexto de una pestaña por su ID * Obtiene el contexto de una pestaña por su ID
*/ */
async getByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>> { async getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>> {
try { try {
return await this._transactionManager.complete(async (transaction) => { return await this._transactionManager.complete(async (transaction) => {
// Verificar si la pestaña existe // Verificar si la pestaña existe

View File

@ -1,11 +1,11 @@
import { AggregateRoot, Result, UniqueID } from "@common/domain"; import { AggregateRoot, Result, UniqueID } from "@common/domain";
import { UserAuthenticatedEvent } from "../events"; import { UserAuthenticatedEvent } from "../events";
import { EmailAddress, PasswordHash, Username } from "../value-objects"; import { EmailAddress, HashPassword, PlainPassword, Username } from "../value-objects";
export interface IAuthenticatedUserProps { export interface IAuthenticatedUserProps {
username: Username; username: Username;
email: EmailAddress; email: EmailAddress;
passwordHash: PasswordHash; hashPassword: HashPassword;
roles: string[]; roles: string[];
} }
@ -19,7 +19,9 @@ export interface IAuthenticatedUser {
isUser: boolean; isUser: boolean;
isAdmin: boolean; isAdmin: boolean;
comparePassword(password: PasswordHash | string): Promise<boolean>; contexts: ICollection<QuoteItem>;
verifyPassword(candidatePassword: PlainPassword): Promise<boolean>;
getRoles(): string[]; getRoles(): string[];
toPersistenceData(): any; toPersistenceData(): any;
} }
@ -45,12 +47,8 @@ export class AuthenticatedUser
return (this._props.roles || []).some((r) => r === role); return (this._props.roles || []).some((r) => r === role);
} }
comparePassword(password: PasswordHash | string): Promise<boolean> { verifyPassword(candidatePassword: PlainPassword): Promise<boolean> {
if (typeof password === "string") { return this._props.hashPassword.verifyPassword(candidatePassword.toString());
return this._props.passwordHash.compare(password);
} else {
return this._props.passwordHash.compare(password.toString());
}
} }
getRoles(): string[] { getRoles(): string[] {
@ -81,10 +79,10 @@ export class AuthenticatedUser
id: this._id.toString(), id: this._id.toString(),
username: this._props.username.toString(), username: this._props.username.toString(),
email: this._props.email.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()), roles: this._props.roles.map((role) => role.toString()),
accessToken: this.accessToken, access_token: this.accessToken,
refreshToken: this.refreshToken, refresh_token: this.refreshToken,
}; };
} }
} }

View File

@ -54,9 +54,10 @@ export class TabContext extends DomainEntity<ITabContextProps> implements ITabCo
toPersistenceData(): any { toPersistenceData(): any {
return { return {
id: this._id.toString(), id: this._id.toString(),
tab_id: this.tabId.toString(),
user_id: this.userId.toString(), user_id: this.userId.toString(),
company_id: this.companyId.toString(), company_id: this.companyId.toString(),
branchId: this.branchId.toString(), branch_id: this.branchId.toString(),
}; };
} }
} }

View File

@ -3,10 +3,7 @@ import { AuthenticatedUser } from "../aggregates";
import { EmailAddress } from "../value-objects"; import { EmailAddress } from "../value-objects";
export interface IAuthenticatedUserRepository { export interface IAuthenticatedUserRepository {
findUserByEmail( getUserByEmail(email: EmailAddress, transaction?: any): Promise<Result<AuthenticatedUser, Error>>;
email: EmailAddress,
transaction?: any
): Promise<Result<AuthenticatedUser, Error>>;
userExists(email: EmailAddress, transaction?: any): Promise<Result<boolean, Error>>; userExists(email: EmailAddress, transaction?: any): Promise<Result<boolean, Error>>;
createUser(user: AuthenticatedUser, transaction?: any): Promise<Result<void, Error>>; createUser(user: AuthenticatedUser, transaction?: any): Promise<Result<void, Error>>;
} }

View File

@ -1,33 +1,33 @@
import { PasswordHash } from "./password-hash"; import { HashPassword } from "./hash-password";
describe("PasswordHash Value Object", () => { describe("PasswordHash Value Object", () => {
it("should hash a valid password", async () => { 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.isSuccess).toBe(true);
expect(result.data.getValue()).not.toBe("StrongPass123"); // Should be hashed expect(result.data.getValue()).not.toBe("StrongPass123"); // Should be hashed
}); });
it("should return an error for short password", async () => { 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.isSuccess).toBe(true);
expect(result.error.message).toBe("Password must be at least 6 characters long"); expect(result.error.message).toBe("Password must be at least 6 characters long");
}); });
it("should validate password comparison correctly", async () => { it("should validate password comparison correctly", async () => {
const result = await PasswordHash.create("SecurePass123"); const result = HashPassword.create("SecurePass123");
expect(result.isSuccess).toBe(true); expect(result.isSuccess).toBe(true);
const isValid = await result.data.compare("SecurePass123"); const isValid = await result.data.verifyPassword("SecurePass123");
expect(isValid).toBe(true); expect(isValid).toBe(true);
}); });
it("should fail password comparison for incorrect passwords", async () => { 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); expect(result.isSuccess).toBe(true);
const isValid = await result.data.compare("WrongPassword"); const isValid = await result.data.verifyPassword("WrongPassword");
expect(isValid).toBe(false); expect(isValid).toBe(false);
}); });
}); });

View File

@ -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);
}
}

View File

@ -1,4 +1,5 @@
export * from "./auth-user-roles"; export * from "./auth-user-roles";
export * from "./email-address"; export * from "./email-address";
export * from "./password-hash"; export * from "./hash-password";
export * from "./plain-password";
export * from "./username"; export * from "./username";

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -1,7 +1,8 @@
import { Result } from "@common/domain"; import { Result } from "@common/domain";
import { AuthenticatedUser } from "@contexts/auth/domain"; import { AuthenticatedUser } from "@contexts/auth/domain";
import { AuthUserModel } from "../sequelize";
export interface IAuthenticatedUserMapper { export interface IAuthenticatedUserMapper {
toDomain(entity: any): Result<AuthenticatedUser, Error>; toDomain(entity: AuthUserModel): Result<AuthenticatedUser, Error>;
toPersistence(aggregate: AuthenticatedUser): any; toPersistence(aggregate: AuthenticatedUser): AuthUserModel;
} }

View File

@ -1,12 +1,13 @@
import { Result, UniqueID } from "@common/domain"; 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"; import { IAuthenticatedUserMapper } from "./authenticated-user-mapper.interface";
export class AuthenticatedUserMapper implements IAuthenticatedUserMapper { export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
/** /**
* 🔹 Convierte una entidad de la base de datos en un agregado de dominio `AuthenticatedUser` * 🔹 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) { if (!entity) {
return Result.fail(new Error("Entity not found")); return Result.fail(new Error("Entity not found"));
} }
@ -14,7 +15,7 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
// Crear Value Objects asegurando que sean válidos // Crear Value Objects asegurando que sean válidos
const uniqueIdResult = UniqueID.create(entity.id); const uniqueIdResult = UniqueID.create(entity.id);
const usernameResult = Username.create(entity.username); 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); const emailResult = EmailAddress.create(entity.email);
// Validar que no haya errores en la creación de los Value Objects // Validar que no haya errores en la creación de los Value Objects
@ -33,7 +34,7 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
{ {
username: usernameResult.data!, username: usernameResult.data!,
email: emailResult.data!, email: emailResult.data!,
passwordHash: passwordHashResult.data!, hashPassword: passwordHashResult.data!,
roles: entity.roles || [], roles: entity.roles || [],
}, },
uniqueIdResult.data! uniqueIdResult.data!
@ -43,7 +44,7 @@ export class AuthenticatedUserMapper implements IAuthenticatedUserMapper {
/** /**
* 🔹 Convierte un agregado `AuthenticatedUser` en un objeto listo para persistencia * 🔹 Convierte un agregado `AuthenticatedUser` en un objeto listo para persistencia
*/ */
toPersistence(authenticatedUser: AuthenticatedUser): any { toPersistence(authenticatedUser: AuthenticatedUser): AuthUserModel {
return authenticatedUser.toPersistenceData(); return authenticatedUser.toPersistenceData();
} }
} }

View File

@ -1,7 +1,8 @@
import { Result } from "@common/domain"; import { Result } from "@common/domain";
import { TabContext } from "@contexts/auth/domain"; import { TabContext } from "@contexts/auth/domain";
import { TabContextModel } from "../sequelize";
export interface ITabContextMapper { export interface ITabContextMapper {
toDomain(entity: any): Result<TabContext, Error>; toDomain(entity: TabContextModel): Result<TabContext, Error>;
toPersistence(aggregate: TabContext): any; toPersistence(aggregate: TabContext): TabContextModel;
} }

View File

@ -1,9 +1,10 @@
import { Result, UniqueID } from "@common/domain"; import { Result, UniqueID } from "@common/domain";
import { TabContext } from "@contexts/auth/domain"; import { TabContext } from "@contexts/auth/domain";
import { TabContextModel } from "../sequelize";
import { ITabContextMapper } from "./tab-context-mapper.interface"; import { ITabContextMapper } from "./tab-context-mapper.interface";
export class TabContextMapper implements ITabContextMapper { export class TabContextMapper implements ITabContextMapper {
toDomain(entity: any): Result<TabContext, Error> { toDomain(entity: TabContextModel): Result<TabContext, Error> {
if (!entity) { if (!entity) {
return Result.fail(new Error("Entity not found")); return Result.fail(new Error("Entity not found"));
} }
@ -39,7 +40,7 @@ export class TabContextMapper implements ITabContextMapper {
); );
} }
toPersistence(tabContext: TabContext): any { toPersistence(tabContext: TabContext): TabContextModel {
return tabContext.toPersistenceData(); return tabContext.toPersistenceData();
} }
} }

View File

@ -5,6 +5,7 @@ import {
AuthenticatedUser, AuthenticatedUser,
EmailAddress, EmailAddress,
IAuthenticatedUserRepository, IAuthenticatedUserRepository,
PlainPassword,
} from "@contexts/auth/domain"; } from "@contexts/auth/domain";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import passport from "passport"; import passport from "passport";
@ -20,6 +21,22 @@ export class PassportAuthProvider implements IAuthProvider {
private readonly _repository: IAuthenticatedUserRepository; private readonly _repository: IAuthenticatedUserRepository;
private readonly _transactionManager!: ITransactionManager; 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 * 🔹 Configura PassportJS
*/ */
@ -31,10 +48,9 @@ export class PassportAuthProvider implements IAuthProvider {
passport.use( passport.use(
"jwt", "jwt",
new JwtStrategy(jwtOptions, (payload, done) => { new JwtStrategy(jwtOptions, (tokenPayload, done) => {
try { try {
console.log(payload); return done(null, tokenPayload);
return done(null, payload);
} catch (error) { } catch (error) {
return done(error, false); return done(error, false);
} }
@ -47,7 +63,7 @@ export class PassportAuthProvider implements IAuthProvider {
{ usernameField: "email", passwordField: "password" }, { usernameField: "email", passwordField: "password" },
async (email, password, done) => { async (email, password, done) => {
try { try {
const user = await this.verifyUser(email, password); const user = await this._verifyUser(email, password);
return user return user
? done(null, user) ? done(null, user)
: done(null, false, { message: "Invalid email or password" }); : done(null, false, { message: "Invalid email or password" });
@ -67,30 +83,17 @@ export class PassportAuthProvider implements IAuthProvider {
this.initializePassport(); this.initializePassport();
} }
generateAccessToken(payload: any): string { generateAccessToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: ACCESS_EXPIRATION }); 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 }); return jwt.sign(payload, SECRET_KEY, { expiresIn: REFRESH_EXPIRATION });
} }
verifyToken(token: string): any { verifyToken(token: string): any {
return jwt.verify(token, SECRET_KEY); 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 = ( export const createPassportAuthProvider = (
@ -101,3 +104,5 @@ export const createPassportAuthProvider = (
const _repository = repository || createAuthenticatedUserRepository(); const _repository = repository || createAuthenticatedUserRepository();
return new PassportAuthProvider(_repository, _transactionManager); return new PassportAuthProvider(_repository, _transactionManager);
}; };
export const initializePassportAuthProvide = () => createPassportAuthProvider();

View File

@ -36,9 +36,8 @@ export class AuthUserModel extends Model<
declare id: string; declare id: string;
declare username: string; declare username: string;
declare email: string; declare email: string;
declare password: string; declare hash_password: string;
declare roles: string[]; declare roles: string[];
declare isActive: boolean;
declare contexts: NonAttribute<TabContextModel[]>; declare contexts: NonAttribute<TabContextModel[]>;
} }
@ -58,7 +57,7 @@ export default (sequelize: Sequelize) => {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
password: { hash_password: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
@ -75,10 +74,6 @@ export default (sequelize: Sequelize) => {
this.setDataValue("roles", rawValue); this.setDataValue("roles", rawValue);
}, },
}, },
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
}, },
{ {
sequelize, sequelize,

View File

@ -49,15 +49,16 @@ export class AuthenticatedUserRepository
} }
} }
async findUserByEmail( async getUserByEmail(
email: EmailAddress, email: EmailAddress,
transaction?: Transaction transaction?: Transaction
): Promise<Result<AuthenticatedUser, Error>> { ): Promise<Result<AuthenticatedUser, Error>> {
try { try {
const rawUser: any = await this._findById( const rawUser: any = await this._getBy(
AuthUserModel, AuthUserModel,
"email", "email",
email.toString(), email.toString(),
{},
transaction transaction
); );

View File

@ -1,7 +1,7 @@
import { UniqueID } from "@common/domain"; import { Result, UniqueID } from "@common/domain";
import { ExpressController } from "@common/presentation"; import { ExpressController } from "@common/presentation";
import { createAuthService, IAuthService } from "@contexts/auth/application"; 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"; import { ILoginPresenter, LoginPresenter } from "./login.presenter";
class LoginController extends ExpressController { class LoginController extends ExpressController {
@ -17,16 +17,18 @@ class LoginController extends ExpressController {
async executeImpl() { async executeImpl() {
const tabId = this.req.headers["x-tab-id"]; const tabId = this.req.headers["x-tab-id"];
const emailVO = EmailAddress.create(this.req.body.email); 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)); const tabIdVO = UniqueID.create(String(tabId));
if ([emailVO, passwordHashVO, tabIdVO].some((r) => r.isFailure)) { const resultValidation = Result.combine([emailVO, plainPasswordVO, tabIdVO]);
return this.clientError("Invalid input data");
if (resultValidation.isFailure) {
return this.clientError("Invalid input data", resultValidation.error);
} }
const userOrError = await this._authService.loginUser({ const userOrError = await this._authService.loginUser({
email: emailVO.data, email: emailVO.data,
passwordHash: passwordHashVO.data, plainPassword: plainPasswordVO.data,
tabId: tabIdVO.data, tabId: tabIdVO.data,
}); });

View File

@ -0,0 +1 @@
export * from "./logout.controller";

View File

@ -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);
};

View File

@ -1,6 +1,6 @@
import { ExpressController } from "@common/presentation"; import { ExpressController } from "@common/presentation";
import { createAuthService, IAuthService } from "@contexts/auth/application"; 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"; import { IRegisterPresenter, RegisterPresenter } from "./register.presenter";
class RegisterController extends ExpressController { class RegisterController extends ExpressController {
@ -16,16 +16,16 @@ class RegisterController extends ExpressController {
async executeImpl() { async executeImpl() {
const emailVO = EmailAddress.create(this.req.body.email); const emailVO = EmailAddress.create(this.req.body.email);
const usernameVO = Username.create(this.req.body.username); 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"); return this.clientError("Invalid input data");
} }
const userOrError = await this._authService.registerUser({ const userOrError = await this._authService.registerUser({
username: usernameVO.data, username: usernameVO.data,
email: emailVO.data, email: emailVO.data,
passwordHash: passwordHashVO.data, hashPassword: hashPasswordVO.data,
}); });
if (userOrError.isFailure) { if (userOrError.isFailure) {

View File

@ -10,10 +10,10 @@ interface AuthenticatedRequest extends Request {
} }
// Middleware para autenticar usando passport con el local-jwt strategy // 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 // Comprueba el rol del usuario
const authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => { const _authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => {
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
const user = req.user as AuthenticatedUser; const user = req.user as AuthenticatedUser;
if (!user || !condition(user)) { if (!user || !condition(user)) {
@ -33,14 +33,14 @@ const authorizeUser = (condition: (user: AuthenticatedUser) => boolean) => {
}; };
// Verifica que el usuario esté autenticado // 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 // 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) // Middleware para verificar que el usuario sea administrador o el dueño de los datos (self)
export const checkAdminOrSelf = [ export const validateUserIsAdminOrOwner = [
authenticateJwt, _authenticateJwt,
(req: AuthenticatedRequest, res: Response, next: NextFunction) => { (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
const user = req.user as AuthenticatedUser; const user = req.user as AuthenticatedUser;
const { userId } = req.params; const { userId } = req.params;

View File

@ -1,25 +1,21 @@
import { UniqueID } from "@common/domain";
import { ApiError, ExpressController } from "@common/presentation"; 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 { NextFunction, Request, Response } from "express";
import httpStatus from "http-status"; import httpStatus from "http-status";
export const validateTabHeader = (req: Request, res: Response, next: NextFunction) => { // Extender el Request de Express para incluir el usuario autenticado optionalmente
const tabId = req.headers["x-tab-id"]; interface TabContextRequest extends Request {
if (!tabId) { tabContext?: TabContext;
return ExpressController.errorResponse( }
new ApiError({
status: 401,
title: httpStatus["401"],
name: httpStatus["401_NAME"],
detail: "Tab ID is required",
}),
res
);
}
next();
};
export const validateTabContext = async (req: Request, res: Response, next: NextFunction) => { export const validateTabContextHeader = async (
const tabId = req.headers["x-tab-id"]; req: TabContextRequest,
res: Response,
next: NextFunction
) => {
const tabId = String(req.headers["x-tab-id"]);
if (!tabId) { if (!tabId) {
return ExpressController.errorResponse( return ExpressController.errorResponse(
new ApiError({ new ApiError({
@ -32,7 +28,19 @@ export const validateTabContext = async (req: Request, res: Response, next: Next
); );
} }
const contextOrError = await TabContextRepository.getContextByTabId(tabId); 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) { if (contextOrError.isFailure) {
return ExpressController.errorResponse( return ExpressController.errorResponse(
new ApiError({ new ApiError({
@ -47,6 +55,6 @@ export const validateTabContext = async (req: Request, res: Response, next: Next
const context = contextOrError.data; const context = contextOrError.data;
req.user = { id: context.user_id, company_id: context.company_id }; req.tabContext = context;
next(); next();
}; };

View File

@ -1,5 +1,5 @@
import { validateRequest } from "@common/presentation"; import { validateRequest } from "@common/presentation";
import { validateTabHeader } from "@contexts/auth/presentation"; import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation";
import { createLoginController } from "@contexts/auth/presentation/controllers"; import { createLoginController } from "@contexts/auth/presentation/controllers";
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller"; import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto"; import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
@ -32,6 +32,7 @@ export const authRouter = (appRouter: Router) => {
* @apiGroup Authentication * @apiGroup Authentication
* @apiVersion 1.0.0 * @apiVersion 1.0.0
* *
* @apiHeader {String} Tab ID (x-tab-id)
* @apiBody {String} email User's email address. * @apiBody {String} email User's email address.
* @apiBody {String} password User's password. * @apiBody {String} password User's password.
* *
@ -43,39 +44,27 @@ export const authRouter = (appRouter: Router) => {
authRoutes.post( authRoutes.post(
"/login", "/login",
validateRequest(LoginUserSchema), validateRequest(LoginUserSchema),
validateTabHeader, validateTabContextHeader,
(req, res, next) => { (req, res, next) => {
createLoginController().execute(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);
/** /**
* @api {post} /api/auth/logout Logout user * @api {post} /api/auth/logout Logout user
* @apiName LogoutUser * @apiName LogoutUser
* @apiGroup Authentication * @apiGroup Authentication
* @apiVersion 1.0.0 * @apiVersion 1.0.0
* *
* @apiHeader {String} Tab ID (x-tab-id)
* @apiHeader {String} Authorization Bearer token. * @apiHeader {String} Authorization Bearer token.
* *
* @apiSuccess (200) {String} message Success message. * @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); appRouter.use("/auth", authRoutes);
}; };