From 52be328cf6118909cc37e5625486d5869e6b9469 Mon Sep 17 00:00:00 2001 From: Christophe CAMENSULI Date: Wed, 7 Feb 2024 01:27:06 +0100 Subject: [PATCH] feat: add session --- package-lock.json | 118 ++- package.json | 4 +- src/nodefony/package-lock.json | 99 ++- src/nodefony/package.json | 9 +- src/nodefony/rollup.config.ts | 7 +- src/nodefony/src/Container.ts | 23 +- src/nodefony/src/Error.ts | 2 +- src/nodefony/src/finder/Finder.ts | 4 +- src/nodefony/src/kernel/Kernel.ts | 1 + src/packages/@nodefony/http/index.ts | 4 +- .../@nodefony/http/nodefony/config/config.ts | 52 +- .../http/nodefony/service/http-kernel.ts | 108 ++- .../service/sessions/sessions-service.ts | 281 +++++++ .../http/nodefony/src/context/Context.ts | 62 +- .../nodefony/src/context/http/HttpContext.ts | 101 ++- .../http/nodefony/src/context/http/Request.ts | 3 + .../nodefony/src/context/http/Response.ts | 86 ++- .../nodefony/src/context/http2/Response.ts | 25 +- .../src/context/websocket/Response.ts | 5 + .../src/context/websocket/WebsocketContext.ts | 12 + .../http/nodefony/src/cookies/cookie.ts | 8 +- .../http/nodefony/src/errors/httpError.ts | 47 +- .../http/nodefony/src/session/session.ts | 693 +++++++++++++++++- .../src/session/storage/FileSessionStorage.ts | 219 ++++++ src/packages/@nodefony/http/package-lock.json | 88 +-- src/packages/@nodefony/http/package.json | 4 +- src/packages/@nodefony/http/rollup.config.ts | 6 +- src/packages/@nodefony/security/package.json | 7 +- src/packages/@nodefony/sequelize/package.json | 4 +- 29 files changed, 1727 insertions(+), 355 deletions(-) create mode 100644 src/packages/@nodefony/http/nodefony/service/sessions/sessions-service.ts create mode 100644 src/packages/@nodefony/http/nodefony/src/session/storage/FileSessionStorage.ts diff --git a/package-lock.json b/package-lock.json index b69e053..1411f2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,8 +21,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.1.3", @@ -1827,16 +1827,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", - "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/type-utils": "6.20.0", - "@typescript-eslint/utils": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1862,15 +1862,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", - "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -1890,13 +1890,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", - "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1907,13 +1907,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", - "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1934,9 +1934,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1947,13 +1947,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1975,17 +1975,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", - "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -2000,12 +2000,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", - "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -9200,9 +9200,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -11389,9 +11389,6 @@ "license": "CECILL-B", "dependencies": { "@inquirer/prompts": "4.0.0", - "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", "asciify": "1.3.5", "cli-color": "2.0.3", "cli-table3": "0.6.3", @@ -11405,7 +11402,7 @@ "node-fetch": "3.3.2", "pug": "3.0.2", "rxjs": "7.8.1", - "semver": "7.5.4", + "semver": "7.6.0", "shelljs": "0.8.5", "twig": "1.17.1", "uuid": "9.0.1" @@ -11433,8 +11430,8 @@ "@types/shelljs": "0.8.15", "@types/twig": "1.12.16", "@types/uuid": "9.0.8", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "chai": "=4.3.10", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", @@ -11542,8 +11539,8 @@ "@types/uuid": "9.0.8", "@types/websocket": "1.0.10", "@types/xml2js": "0.4.14", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "rollup-plugin-copy": "3.5.0", @@ -11586,9 +11583,6 @@ "src/packages/@nodefony/security": { "version": "10.0.0", "dependencies": { - "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", "bcrypt": "5.1.1", "csrf": "^3.1.0", "jsonwebtoken": "9.0.2", @@ -11609,8 +11603,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "rollup-plugin-copy": "3.5.0", @@ -11667,8 +11661,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "sequelize-cli": "6.6.2", diff --git a/package.json b/package.json index da33e49..474d10d 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.1.3", diff --git a/src/nodefony/package-lock.json b/src/nodefony/package-lock.json index e6b7c93..092759e 100644 --- a/src/nodefony/package-lock.json +++ b/src/nodefony/package-lock.json @@ -10,9 +10,6 @@ "license": "CECILL-B", "dependencies": { "@inquirer/prompts": "4.0.0", - "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", "asciify": "1.3.5", "cli-color": "2.0.3", "cli-table3": "0.6.3", @@ -26,7 +23,7 @@ "node-fetch": "3.3.2", "pug": "3.0.2", "rxjs": "7.8.1", - "semver": "7.5.4", + "semver": "7.6.0", "shelljs": "0.8.5", "twig": "1.17.1", "uuid": "9.0.1" @@ -54,8 +51,8 @@ "@types/shelljs": "0.8.15", "@types/twig": "1.12.16", "@types/uuid": "9.0.8", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "chai": "=4.3.10", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", @@ -1203,16 +1200,16 @@ "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", - "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/type-utils": "6.20.0", - "@typescript-eslint/utils": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1238,15 +1235,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", - "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -1266,13 +1263,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", - "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1283,13 +1280,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", - "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1310,9 +1307,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1323,13 +1320,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1395,17 +1392,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", - "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -1420,12 +1417,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", - "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -5725,9 +5722,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, diff --git a/src/nodefony/package.json b/src/nodefony/package.json index 3e03fb7..1139edd 100644 --- a/src/nodefony/package.json +++ b/src/nodefony/package.json @@ -63,9 +63,6 @@ }, "dependencies": { "@inquirer/prompts": "4.0.0", - "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", "asciify": "1.3.5", "cli-color": "2.0.3", "cli-table3": "0.6.3", @@ -79,7 +76,7 @@ "node-fetch": "3.3.2", "pug": "3.0.2", "rxjs": "7.8.1", - "semver": "7.5.4", + "semver": "7.6.0", "shelljs": "0.8.5", "twig": "1.17.1", "uuid": "9.0.1" @@ -104,8 +101,8 @@ "@types/shelljs": "0.8.15", "@types/twig": "1.12.16", "@types/uuid": "9.0.8", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "chai": "=4.3.10", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", diff --git a/src/nodefony/rollup.config.ts b/src/nodefony/rollup.config.ts index f8dcc46..b709dfc 100644 --- a/src/nodefony/rollup.config.ts +++ b/src/nodefony/rollup.config.ts @@ -25,6 +25,7 @@ const external: string[] = [ "mime-types", "moment", "node-emoji", + "node-fetch", "rxjs", "semver", "shelljs", @@ -66,7 +67,7 @@ const sharedBinaryOptions = defineConfig({ file: "./bin/nodefony", exports: "default", format: "esm", - externalLiveBindings: false, + //externalLiveBindings: false, //freeze: false, }, onwarn(warning, warn) { @@ -247,8 +248,8 @@ function createNodeConfig(isProduction: boolean): RollupOptions { output: { ...sharedNodeOptions.output, sourcemap: !isProduction, - //preserveModules: true, - //preserveModulesRoot: "src", + preserveModules: !isProduction, + preserveModulesRoot: "src", }, external, plugins: [ diff --git a/src/nodefony/src/Container.ts b/src/nodefony/src/Container.ts index 3a7996b..e1f835a 100644 --- a/src/nodefony/src/Container.ts +++ b/src/nodefony/src/Container.ts @@ -51,22 +51,22 @@ const parseParameterString = function ( }; // Déclaration d'un objet hétérogène -interface DynamicService { +export interface DynamicService { [cleDynamic: string]: any; // Propriétés dynamiques de tout type } -interface DynamicParam { +export interface DynamicParam { [cleDynamic: string]: any; // Propriétés dynamiques de tout type } -interface Scopes { +export interface Scopes { [nameScope: string]: { [idContainer: string]: Container; }; } -type ProtoService = { (): void; [key: string]: any }; -type ProtoParameters = { (): void; [key: string]: any }; +export type ProtoService = { (): void; [key: string]: any }; +export type ProtoParameters = { (): void; [key: string]: any }; type ProtoParametersPrototype = ReturnType< typeof Container.prototype.protoParameters.prototype >; @@ -79,11 +79,9 @@ type ProtoParametersPrototype = ReturnType< class Container { public protoService: ProtoService = function () {}; protected services: ProtoService | null; - public protoParameters: ProtoParameters = function () {}; protected parameters: ProtoService | null; - - private id: string; + protected id: string; private scopes: Scopes = {}; constructor(services: DynamicService = {}, parameters: DynamicParam = {}) { @@ -226,6 +224,13 @@ class Container { this.services = null; this.parameters = null; } + public reset(): void { + this.clean(); + this.protoService = function () {}; + this.protoParameters = function () {}; + this.services = Object.create(this.protoService.prototype); + this.parameters = Object.create(this.protoParameters.prototype); + } } /* @@ -268,4 +273,4 @@ class Scope extends Container { } export default Container; -export { DynamicParam, Scope }; +export { Scope }; diff --git a/src/nodefony/src/Error.ts b/src/nodefony/src/Error.ts index 3a471a9..bf34ddc 100644 --- a/src/nodefony/src/Error.ts +++ b/src/nodefony/src/Error.ts @@ -96,7 +96,7 @@ class nodefonyError extends Error { //public actual : string [key: string]: any; - constructor(message: string | Error, code?: number) { + constructor(message?: string | Error, code?: number) { if (message instanceof Error) { super(message.message); } else { diff --git a/src/nodefony/src/finder/Finder.ts b/src/nodefony/src/finder/Finder.ts index 2e18a94..b17daaf 100644 --- a/src/nodefony/src/finder/Finder.ts +++ b/src/nodefony/src/finder/Finder.ts @@ -272,9 +272,9 @@ class Finder extends Event { public settings: DefaultSettingsInterface; public totals: TotalInterface; - constructor(settings: DefaultSettingsInterface) { + constructor(settings: DefaultSettingsInterface = {}) { super(settings); - this.settings = extend({}, defaultSettings, settings || {}); + this.settings = extend({}, defaultSettings, settings); this.totals = { Directory: 0, File: 0, diff --git a/src/nodefony/src/kernel/Kernel.ts b/src/nodefony/src/kernel/Kernel.ts index 7537cc0..d3cbabe 100644 --- a/src/nodefony/src/kernel/Kernel.ts +++ b/src/nodefony/src/kernel/Kernel.ts @@ -151,6 +151,7 @@ class Kernel extends Service { undefined, //cli.notificationsCenter as Event, extend({}, kernelDefaultOptions, options) ); + this.setMaxListeners(30); Nodefony.setKernel(this); this.kernel = this; this.set("kernel", this); diff --git a/src/packages/@nodefony/http/index.ts b/src/packages/@nodefony/http/index.ts index 2eb75fe..ccf40e6 100644 --- a/src/packages/@nodefony/http/index.ts +++ b/src/packages/@nodefony/http/index.ts @@ -7,6 +7,7 @@ import WebsocketServer from "./nodefony/service/servers/server-websocket"; import WebsocketSecureServer from "./nodefony/service/servers/server-websocket-secure"; import StaticServer from "./nodefony/service/servers/server-static"; import networkCommand from "./nodefony/command/networkCommand"; +import sessionService from "./nodefony/service/sessions/sessions-service"; import { fileURLToPath } from "url"; import Certificate from "./nodefony/service/certificates"; @@ -17,8 +18,9 @@ class Http extends Module { super("http", kernel, fileURLToPath(import.meta.url), config); this.httpKernel = null; this.addCommand(networkCommand); - this.httpKernel = this.addService(HttpKernel) as HttpKernel; + this.httpKernel = this.addService(HttpKernel, this.kernel) as HttpKernel; this.addService(Certificate, this.httpKernel); + this.addService(sessionService, this.httpKernel); } async onStart(): Promise { diff --git a/src/packages/@nodefony/http/nodefony/config/config.ts b/src/packages/@nodefony/http/nodefony/config/config.ts index bda819f..bad06ff 100644 --- a/src/packages/@nodefony/http/nodefony/config/config.ts +++ b/src/packages/@nodefony/http/nodefony/config/config.ts @@ -1,47 +1,6 @@ import { kernel } from "nodefony"; import path from "node:path"; import fs from "node:fs"; -import { Hash, createHash } from "node:crypto"; - -const readFile = function (Path: fs.PathOrFileDescriptor): string { - try { - return fs.readFileSync(Path, { - encoding: "utf8", - }); - } catch (e) { - console.error(e); - throw e; - } -}; - -const createSecret = function (cwd: string = process.cwd()): string { - const sercretPath = path.resolve( - cwd, - "nodefony", - "config", - "certificates", - "server", - "private.key.pem" - ); - return createHash("sha512") - .update(readFile(sercretPath)) - .digest("base64") - .substr(0, 32); -}; - -const createIv = function () { - const sercretPath = path.resolve( - "nodefony", - "config", - "certificates", - "server", - "public.key.pem" - ); - return createHash("sha512") - .update(readFile(sercretPath)) - .digest("base64") - .substr(0, 16); -}; const tmpDir = kernel?.tmpDir.path || "/tmp"; @@ -174,12 +133,12 @@ export default { */ session: { applyTransaction: true, // sequelize transaction session entity - start: false, // false || true || Name Session Context + start: true, // false || true || Name Session Context use_strict_mode: true, name: "nodefony", handler: "files", // files | orm | memcached "nodefony.session.storage" save_path: "/tmp/sessions", - gc_probability: 1, + gc_probability: 5, gc_divisor: 100, gc_maxlifetime: 1440, hash_function: "md5", // sha1 @@ -189,14 +148,9 @@ export default { cookie: { maxAge: 0, // like cookie_lifetime =>seconde httpOnly: true, // don't see by script (javascript) - secure: false, // https only + secure: true, // https only signed: false, }, - // encrypt: { - // algorithm: "aes-256-ctr", - // password: createSecret(), - // iv: createIv(), - // }, /** * SERVICE memcached diff --git a/src/packages/@nodefony/http/nodefony/service/http-kernel.ts b/src/packages/@nodefony/http/nodefony/service/http-kernel.ts index cd2938f..f6a09c4 100644 --- a/src/packages/@nodefony/http/nodefony/service/http-kernel.ts +++ b/src/packages/@nodefony/http/nodefony/service/http-kernel.ts @@ -4,6 +4,7 @@ import nodefony, { Container, Event, Scope, + Kernel, typeOf, } from "nodefony"; import HttpError from "../src/errors/httpError"; @@ -22,6 +23,8 @@ import clc from "cli-color"; import Certicates from "./certificates"; import websocket from "websocket"; +import SessionsService from "./sessions/sessions-service"; +import Session from "../src/session/session"; export type ProtocolType = "1.1" | "2.0" | "3.0"; export type httpRequest = http.IncomingMessage | http2.Http2ServerRequest; @@ -58,6 +61,7 @@ class HttpKernel extends Service { module: Module; httpsPort?: number; httpPort?: number; + responseTimeout: { http: number; https: number; @@ -68,9 +72,11 @@ class HttpKernel extends Service { ws: number; wss: number; }; - constructor(module: Module) { - const container: Container = module.container as Container; - const event: Event = module.notificationsCenter as Event; + sessionService?: SessionsService; + sessionAutoStart: boolean | string = false; + constructor(module: Module, kernel: Kernel) { + const container: Container = kernel.container as Container; + const event: Event = kernel.notificationsCenter as Event; super(serviceName, container, event, module.options); this.module = module; this.container?.addScope("request"); @@ -80,6 +86,8 @@ class HttpKernel extends Service { this.domain = this.kernel?.domain as string; this.domainAlias = this.kernel?.options?.domainAlias; this.regAlias = this.compileAlias(); + this.sessionService = this.get("sessions"); + this.sessionAutoStart = this.sessionService?.sessionAutoStart as boolean; }); this.responseTimeout = { http: this.options.http.responseTimeout, @@ -107,7 +115,6 @@ class HttpKernel extends Service { case "http2": log = clc.cyan.bgBlue(`${(request as httpRequest).url}`); this.log(`${log}`, "DEBUG", `${type}`); - return this.handleHttp( scope as Scope, request as httpRequest, @@ -129,32 +136,41 @@ class HttpKernel extends Service { async handleFrontController(context: ContextType): Promise {} async onError(error: Error | HttpError, context: ContextType): Promise { - const code = error.code === 200 ? 500 : !error.code ? 500 : error.code; - if (!(error instanceof HttpError)) { - error = new HttpError(error as Error, error.code, context); - } - error.code = code; - let log = ""; - switch (true) { - case context instanceof HttpContext: { - context.response.setStatusCode(code, error.message); - if (this.kernel?.debug) { - this.log(error.toString(), "ERROR"); - } else { - this.log(error.message, "ERROR"); - } - return await context.send(error.message); + try { + const code = error.code === 200 ? 500 : !error.code ? 500 : error.code; + if (!(error instanceof HttpError)) { + error = new HttpError(error as Error, error.code, context); + } + error.code = code; + if (context) { + context.error = error; } - case context instanceof WebsocketContext: { - if (this.kernel?.debug) { - this.log(error.toString(), "ERROR"); - } else { - this.log(error.message, "ERROR"); + switch (true) { + case context instanceof HttpContext: { + context.response.setStatusCode(code, error.message); + if (this.kernel?.debug) { + this.log(error.toString(), "ERROR"); + } else { + if (error.message) this.log(error.message, "ERROR"); + } + context.response.setContentType("text"); + return context.send(error.message).catch((e) => { + throw e; + }); + } + case context instanceof WebsocketContext: { + if (this.kernel?.debug) { + this.log(error.toString(), "ERROR"); + } else { + this.log(error.message, "ERROR"); + } + return; } - return; + default: + throw error; } - default: - throw error; + } catch (e) { + throw e; } } @@ -261,6 +277,28 @@ class HttpKernel extends Service { return servers; } + async startSession( + context: WebsocketContext | HttpContext + ): Promise { + if ( + this.sessionService && + (this.sessionAutoStart || context.hasSession()) + ) { + return this.sessionService + .start(context, this.sessionAutoStart as string) + .then((session: Session | null) => { + // if (this.firewall) { + // this.firewall.getSessionToken(context, session); + // } + return session; + }) + .catch((e) => { + throw e; + }); + } + return Promise.resolve(null); + } + createHttpContext( scope: Scope, request: httpRequest, @@ -285,9 +323,9 @@ class HttpKernel extends Service { context.clean(); }); } - return context; } catch (e) { + console.trace(e); this.log(e, "ERROR"); throw e; } @@ -351,6 +389,20 @@ class HttpKernel extends Service { if (this.kernel?.options.domainCheck) { this.checkValidDomain(context); } + // SESSIONS + try { + await this.startSession(context); + // CSRF TOKEN + // if (context.csrf) { + // const token = await this.csrfService.handle(context); + // if (token) { + // this.log("CSRF TOKEN OK", "DEBUG"); + // } + // } + return resolve(context); + } catch (e) { + return reject(e); + } return resolve(context); // // FRONT CONTROLLER // const ret = await this.handleFrontController(context).catch((e) => { diff --git a/src/packages/@nodefony/http/nodefony/service/sessions/sessions-service.ts b/src/packages/@nodefony/http/nodefony/service/sessions/sessions-service.ts new file mode 100644 index 0000000..808e904 --- /dev/null +++ b/src/packages/@nodefony/http/nodefony/service/sessions/sessions-service.ts @@ -0,0 +1,281 @@ +import nodefony, { + extend, + Service, + Kernel, + Container, + Event, + Module, + FamilyType, + DynamicService, + ProtoService, + ProtoParameters, +} from "nodefony"; +import HttpKernel, { + ProtocolType, + ServerType, + ContextType, + httpRequest, +} from "../http-kernel"; +import Context, { HTTPMethod } from "../../src/context/Context"; +import Session, { OptionsSessionType } from "../../src/session/session"; +import Http2Resquest from "../../src/context/http2/Request"; +import HttpResquest from "../../src/context/http/Request"; +import url from "node:url"; +import Certificate from "../../service/certificates"; +import { createHash } from "node:crypto"; + +import FileSessionStorage from "../../src/session/storage/FileSessionStorage"; + +export type sessionStrategyType = "none" | "migrate" | "invalidate"; +export type sessionStorageType = any; // "orm" | "memcached" | "redis" | "fileSystem"; + +export type FlashBagSessionType = Record; +export type MetaBagSessionType = Record; +export interface SerializeSessionType { + Attributes: ProtoService; + metaBag: ProtoParameters; + flashBag: FlashBagSessionType; + user: string; +} + +export interface sessionStorageInterface { + read: (name: string) => Promise; + write: ( + name: string, + serialize: SerializeSessionType, + contextSession: string + ) => Promise; + start: (id: string, contextSession: string) => Promise; + open: (contextSession: string) => Promise; + close: () => boolean; + destroy: (id: string, contextSession: string) => Promise; + gc: (maxlifetime: number, contextSession: string) => Promise; +} + +class SessionsService extends Service { + httpKernel: HttpKernel | null = null; + sessionStrategy: sessionStrategyType = "migrate"; + storage: any = null; + gc_probability: number = 1; + gc_divisor: number = 100; + module: Module; + defaultSessionName: string = "nodefony"; + sessionAutoStart: string | boolean = false; + secret?: Buffer; + iv?: Buffer; + certificates: Certificate; + constructor(module: Module, httpKernel: HttpKernel) { + super( + "sessions", + httpKernel.container as Container, + httpKernel.notificationsCenter as Event, + module.options.session + ); + this.module = module; + this.certificates = this.get("certificates"); + this.httpKernel = httpKernel; + this.gc_probability = + this.options.gc_probability === "string" + ? parseInt(this.options.gc_probability, 10) + : this.options.gc_probability; + this.gc_divisor = this.options.gc_divisor; + this.defaultSessionName = this.options.name; + this.sessionAutoStart = this.setAutoStart(this.options.start); + + this.once("onTerminate", () => { + if (this.storage) { + this.storage.close(); + } + }); + this.once("onStart", () => { + this.secret = this.createSecret(); + this.iv = this.createIv(); + this.initializeStorage(); + }); + } + + initializeStorage(): sessionStorageInterface { + let storage: any = null; + switch (this.options.handler) { + case "orm": + case "ORM": + //storage = nodefony.session.storage[this.kernel?.getOrm()]; + break; + case "files": + storage = FileSessionStorage; + break; + default: + throw new Error(`Session Storage not found `); + } + try { + if (storage) { + this.storage = new storage(this); + this.on("onReady", async () => { + await this.storage.open("default"); + }); + } else { + this.storage = null; + this.log( + `SESSION HANDLER STORAGE NOT FOUND :${this.options.handler}`, + "ERROR" + ); + } + return this.storage; + } catch (e) { + throw e; + } + } + + createSecret(): Buffer { + const secret = createHash("sha512") + .update(this.certificates?.key as Buffer) + .digest(); + return Buffer.from(secret.buffer.slice(0, 32)); + } + + createIv(): Buffer { + const iv = createHash("sha512") + .update(this.certificates?.publicKeyPem as Buffer) + .digest(); + return Buffer.from(iv.buffer.slice(0, 16)); + } + + setAutoStart(auto: string | null | undefined | boolean): string | false { + switch (auto) { + case true: + case "": + case undefined: + return "default"; + case false: + case null: + return false; + default: + if (typeof auto === "string") { + return auto; + } + throw new Error(`Session start settings config error : ${auto}`); + } + } + + async start( + context: ContextType, + sessionContext: string + ): Promise { + return new Promise((resolve, reject) => { + if (context.sessionStarting) { + if (context.session) { + resolve(context.session); + return; + } + context.once("onSessionStart", (session: Session, error: Error) => { + if (session) { + return resolve(session); + } + return reject(error || new Error("Bad Session")); + }); + return; + } + if (context.session) { + if (context.session.status === "active") { + this.log( + `SESSION ALLREADY STARTED ==> ${context.session.name} : ${context.session.id}`, + "DEBUG" + ); + resolve(context.session); + return; + } + } + let inst = null; + try { + context.sessionStarting = true; + sessionContext = this.setAutoStart(sessionContext) as string; + if (this.probaGarbage()) { + this.storage.gc(this.options.gc_maxlifetime, sessionContext); + } + inst = this.createSession(this.defaultSessionName); + } catch (e) { + context.fire("onSessionStart", null, e); + reject(e); + throw e; + } + inst + .start(context, sessionContext) + .then((session) => { + try { + context.session = session; + const method = context.method as HTTPMethod; + const request = context.request as HttpResquest | Http2Resquest; + if (method !== "WEBSOCKET" && request && request.request) { + request.request.session = session; + } + context.sessionStarting = false; + session.setMetaBag("url", url.parse(context.url)); + if (context.cleaned) { + return reject(new Error("context already cleaned")); + } + context.fire("onSessionStart", session, null); + return resolve(session); + } catch (e) { + if (context.cleaned) { + return reject(e); + } + context.fire("onSessionStart", null, e); + return reject(e); + } + }) + .catch((err) => { + console.log(err); + if (context.cleaned) { + return reject(err); + } + context.fire("onSessionStart", null, err); + return reject(err); + }); + }); + } + + saveSession(context: ContextType): Promise { + if (context.session) { + if (!context.session.saved) { + return context.session.save( + context.user ? context.user : null, + context.session.contextSession + ); + } + } + return Promise.resolve(null); + } + + createSession(name: string, options?: OptionsSessionType): Session { + try { + options = extend({}, this.options, options); + return new Session(name, options as OptionsSessionType, this); + } catch (e) { + throw e; + } + } + + addContextSession(context: ContextType) { + if (this.storage) { + this.once("onReady", () => { + this.storage.open(context); + }); + } + } + + setSessionStrategy(strategy: sessionStrategyType) { + this.sessionStrategy = strategy; + } + + probaGarbage(): boolean { + // Génère un nombre aléatoire entre 0 et 100 + const random = Math.floor(Math.random() * 100) + 1; + // Si le nombre aléatoire est inférieur ou égal à gc_probability + if (random <= this.gc_probability) { + return true; + } + return false; + } +} + +export default SessionsService; diff --git a/src/packages/@nodefony/http/nodefony/src/context/Context.ts b/src/packages/@nodefony/http/nodefony/src/context/Context.ts index 84c612e..6029fdf 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/Context.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/Context.ts @@ -6,6 +6,7 @@ import nodefony, { Message, Pdu, KernelEventsType, + Error as nodefonyError, } from "nodefony"; import websocket from "websocket"; import HttpKernel, { ContextType, ServerType } from "../../service/http-kernel"; @@ -14,12 +15,14 @@ import Http2Response from "./http2/Response"; import WebsocketResponse from "./websocket/Response"; import HttpResquest from "./http/Request"; import Http2Resquest from "./http2/Request"; +import SessionsService from "../../service/sessions/sessions-service"; import clc from "cli-color"; import http from "node:http"; import http2 from "node:http2"; import { URL } from "node:url"; import Session from "../session/session"; -import Cookie from "../cookies/cookie"; +import Cookie, { cookiesParser } from "../cookies/cookie"; +import HttpError from "../errors/httpError"; const colorLogEvent = clc.cyan.bgBlue("EVENT CONTEXT"); export type contextRequest = @@ -42,7 +45,8 @@ export type HTTPMethod = | "CONNECT" | "OPTIONS" | "TRACE" - | "PATCH"; + | "PATCH" + | "WEBSOCKET"; class Context extends Service { secure: boolean = false; @@ -50,11 +54,11 @@ class Context extends Service { isControlledAccess: boolean = false; validDomain: boolean = false; finished: boolean = false; - errorLog: boolean = false; contentLength: boolean = false; pushAllowed: boolean = false; requestEnded: boolean = false; requested: boolean = false; + sessionStarting: boolean = false; domain: string = ""; type: ServerType; httpKernel: HttpKernel | null; @@ -65,15 +69,21 @@ class Context extends Service { remoteAddress: string | undefined | null = null; originUrl: URL | undefined | null = null; cookies: Record = {}; + error: Error | HttpError | nodefonyError | null | undefined = null; + sessionService?: SessionsService; + session: Session | null | undefined = null; + cookieSession: Cookie | null | undefined = null; + user: any = null; constructor(container: Container, type: ServerType) { super(`${type} CONTEXT`, container); this.type = type; this.set("context", this); this.httpKernel = this.get("httpKernel"); - this.container?.addScope("subRequest"); - this.once("onRequest", () => { - this.requested = true; - }); + this.sessionService = this.get("sessions"); + // this.container?.addScope("subRequest"); + // this.once("onRequest", () => { + // this.requested = true; + // }); } log(pci: any, severity?: Severity, msgid?: Msgid, msg?: Message): Pdu { @@ -82,6 +92,7 @@ class Context extends Service { } return super.log(pci, severity, msgid, msg); } + clean(): void { this.cleaned = true; this.httpKernel = null; @@ -112,12 +123,15 @@ class Context extends Service { return super.emitAsync(event, ...args); } - logRequest(httpError?: nodefony.Error) { + logRequest(httpError?: Error | HttpError | nodefonyError) { try { const txt = `${clc.cyan("URL")} : ${this.url} ${clc.cyan("FROM")} : ${this.remoteAddress} ${clc.cyan("ORIGIN")} : ${this.originUrl?.host}`; let mgid = ""; + if (!httpError && this.error) { + httpError = this.error; + } if (httpError) { - this.errorLog = true; + this.error = httpError; mgid = `${this.type} ${clc.magenta(this.response?.statusCode)} ${clc.red(this.method)}`; if (this.kernel && this.kernel.environment === "prod") { return this.log(`${txt} ${httpError.toString()}`, "ERROR", mgid); @@ -129,7 +143,7 @@ class Context extends Service { mgid ); } - if (!this.errorLog) { + if (!this.error) { mgid = `${this.type} ${clc.magenta(this.response?.statusCode)} ${this.method}`; return this.log(txt, "INFO", mgid); } @@ -146,6 +160,12 @@ class Context extends Service { } } + setCookie(cookie: Cookie) { + if (cookie) { + return this.response?.addCookie(cookie); + } + } + getRequest(): contextRequest { return this.request; } @@ -160,8 +180,26 @@ class Context extends Service { return this.httpKernel.isValidDomain(this); } - async saveSession(): Promise { - return new Session(); + async saveSession(): Promise { + if (this.sessionService) { + return this.sessionService.saveSession(this); + } + throw new Error(`sessionService not found `); + } + + hasSession(): boolean { + return Boolean(this.cookieSession); + } + + getCookieSession(name: string): Cookie | null { + if (this.cookies[name]) { + return this.cookies[name]; + } + return null; + } + + parseCookies(): void { + return cookiesParser(this); } } diff --git a/src/packages/@nodefony/http/nodefony/src/context/http/HttpContext.ts b/src/packages/@nodefony/http/nodefony/src/context/http/HttpContext.ts index bd965b4..c2a9ad7 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/http/HttpContext.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/http/HttpContext.ts @@ -55,7 +55,9 @@ class HttpContext extends Context { isHtml: boolean = false; request: HttpRequestType; response: HttpRsponseType; - session: Session | null = null; + resolver: any | null = null; + router: any | null = null; + isJson: boolean = false; constructor( container: Container, request: http.IncomingMessage | http2.Http2ServerRequest, @@ -106,8 +108,13 @@ class HttpContext extends Context { ); } this.isHtml = this.request.acceptHtml; + this.setDefaultContentType(); this.domain = this.getHostName(); this.validDomain = this.isValidDomain(); + this.parseCookies(); + this.cookieSession = this.getCookieSession( + this.sessionService?.defaultSessionName as string + ); this.once("onTimeout", () => { let error = null; @@ -124,6 +131,7 @@ class HttpContext extends Context { handle(/*data*/): Promise { return new Promise(async (resolve, reject) => { try { + this.setTimeout(); if (this.isRedirect) { await this.send(); return resolve(this); @@ -137,25 +145,16 @@ class HttpContext extends Context { } this.setParameters("query.request", this.request.query); //this.locale = this.translation.handle(); - // if (!this.resolver) { - // this.resolver = this.router.resolve(this); - // } // WARNING EVENT KERNEL this.fire("onRequest", this); this.kernel?.fire("onRequest", this); - this.setTimeout(); - //this.response.setHeader("Content-Type", "text/plain"); - //this.response.setStatusCode(404); - //await this.send(`${this.response.statusMessage}`); - throw new Error(`sksksksk`); - // if (this.resolver.resolve) { - // const ret = this.resolver.callController(data); - // return resolve(ret); - // } - // const error = new Error(""); - // error.code = 404; - // return reject(error); - return resolve(this); + if (!this.resolver && this.router) { + this.resolver = this.router.resolve(this); + } + if (this.resolver && this.resolver.resolve) { + return resolve(this.resolver.callController()); + } + return reject(new HttpError("", 404, this)); } catch (e) { return reject(e); } @@ -177,27 +176,38 @@ class HttpContext extends Context { ): Promise< http.ServerResponse | http2.ServerHttp2Stream > { - if (this.sended || this.finished) { - return new Promise((resolve, reject) => { - reject(new Error("Already sended")); - }); - } + // if (this.sended || this.finished) { + // return new Promise((resolve, reject) => { + // reject(new Error("Already sended")); + // }); + // } return this.saveSession() - .then(async (session) => { + .then(async (session: Session | null) => { if (session) { //this.log(`SAVE SESSION ID : ${session.id}`, "DEBUG"); } await this.fireAsync("onSend", this.response, this); - this.writeHead(); + try { + this.writeHead(); + } catch {} if (!this.isRedirect) { - return this.write(chunk, encoding); + return this.write(chunk, encoding).catch((e) => { + throw e; + }); } - return this.response.end(); + return this.response.end().catch((e) => { + return this.write(e.message); + }); }) .catch(async (error) => { this.log(error, "ERROR"); - this.writeHead(error.code || 500); - this.write(chunk, encoding); + try { + this.writeHead(error.code || 500); + } catch {} + + await this.write(error.message, encoding).catch((e) => { + throw e; + }); return error; }); } @@ -208,7 +218,7 @@ class HttpContext extends Context { ) { // cookies if (this.response) { - //this.response.setCookies(); + this.response.setCookies(); this.response.writeHead(statusCode, headers); } } @@ -220,16 +230,24 @@ class HttpContext extends Context { ): Promise< http.ServerResponse | http2.ServerHttp2Stream > { - if (this.finished || this.sended) { - throw new Error(`Already sended `); - } + // if (this.finished || this.sended) { + // throw new Error(`Already sended `); + // } /* * WRITE RESPONSE */ - this.sended = true; - this.response.send(chunk, encoding); + await this.response + .send(chunk, encoding) + .then(() => { + this.sended = true; + }) + .catch((e) => { + throw e; + }); // END REQUEST - return this.close(); + return this.close().catch((e) => { + throw e; + }); } flush(chunk: any, encoding: BufferEncoding) { @@ -241,7 +259,9 @@ class HttpContext extends Context { > { await this.fireAsync("onClose", this); // END REQUEST - return this.response.end(); + return this.response.end().catch((e) => { + throw e; + }); } redirect( @@ -328,6 +348,15 @@ class HttpContext extends Context { getMethod(): HTTPMethod { return this.request?.getMethod(); } + + setDefaultContentType() { + if (this.isHtml) { + this.response.setContentType("html", "utf-8"); + } else if (this.request.accepts("json")) { + this.isJson = true; + this.response.setContentType("json", "utf-8"); + } + } } export default HttpContext; diff --git a/src/packages/@nodefony/http/nodefony/src/context/http/Request.ts b/src/packages/@nodefony/http/nodefony/src/context/http/Request.ts index bfc964e..19c03c9 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/http/Request.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/http/Request.ts @@ -9,6 +9,7 @@ import formidable, { IncomingForm } from "formidable"; import { Container } from "nodefony"; import { ParserXml, ParserQs, Parser, acceptParser } from "./parser"; import nodefony, { extend, Pdu, Message, Severity, Msgid } from "nodefony"; +import Session from "../../session/session"; const reg = /(.*)[\[][\]]$/u; @@ -27,12 +28,14 @@ declare module "url" { declare module "http" { interface IncomingMessage { body: any; + session: Session; } } declare module "http2" { interface Http2ServerRequest { body: any; + session: Session; } } diff --git a/src/packages/@nodefony/http/nodefony/src/context/http/Response.ts b/src/packages/@nodefony/http/nodefony/src/context/http/Response.ts index 1812e42..877944e 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/http/Response.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/http/Response.ts @@ -4,6 +4,7 @@ import HttpContext from "../http/HttpContext"; import nodefony, { extend, Pdu, Message, Severity, Msgid } from "nodefony"; import mime from "mime-types"; import { responseTimeoutType } from "../../../service/http-kernel"; +import Cookie from "../../cookies/cookie"; const ansiRegex = function ({ onlyFirst = false } = {}) { const pattern = [ @@ -29,7 +30,7 @@ class HttpResponse { contentType: string = "application/octet-stream"; headers: http.OutgoingHttpHeaders = {}; timeout?: number; // miiliseconde - cookies?: any; + cookies: Record = {}; constructor( response: http.ServerResponse | http2.Http2ServerResponse, context: HttpContext @@ -40,14 +41,13 @@ class HttpResponse { this.context?.httpKernel?.responseTimeout[ this.context.type as responseTimeoutType ]; + this.setContentTypeByExtension("bin"); } clean() { this.response = null; - this.cookies = null; - delete this.cookies; this.body = null; - + this.cookies = {}; // this.streamFile = null; // delete this.streamFile; } @@ -61,15 +61,43 @@ class HttpResponse { this.timeout = ms; } - addCookie() {} + addCookie(cookie: Cookie) { + if (cookie instanceof Cookie) { + return (this.cookies[cookie.name] = cookie); + } + throw new Error("Response addCookies not valid cookies"); + } - deleteCookie() {} + deleteCookie(cookie: Cookie) { + if (cookie instanceof Cookie) { + if (this.cookies[cookie.name]) { + delete this.cookies[cookie.name]; + return true; + } + return false; + } + throw new Error("Response delCookie not valid cookies"); + } - deleteCookieByName() {} + deleteCookieByName(name: string) { + if (this.cookies[name]) { + delete this.cookies[name]; + return true; + } + return false; + } - setCookies() {} + setCookies() { + for (const cook in this.cookies) { + this.setCookie(this.cookies[cook]); + } + } - setCookie() {} + setCookie(cookie: Cookie) { + const serialize = cookie.serialize(); + this.log(`ADD COOKIE ==> ${serialize}`, "DEBUG"); + return this.setHeader("Set-Cookie", serialize); + } // ADD INPLICIT HEADER setHeader(name: string, value: string | number) { @@ -100,7 +128,32 @@ class HttpResponse { return (this.headers = this.response.getHeaders()); } - setContentType(type: string, encoding: BufferEncoding) { + setContentType(type?: string, encoding?: BufferEncoding) { + if (type && encoding) { + const mytype = mime.contentType(type); + if (!mytype) { + this.log(`setContentType: ${type} not found`, "WARNING"); + } + return this.setHeader("Content-Type", `${type}; charset=${encoding}`); + } + if (type && !encoding) { + const mytype = mime.contentType(type); + if (mytype) { + this.contentType = mytype; + let charset = mime.charset(this.contentType); + if (charset) { + this.encoding = charset as BufferEncoding; + } + return this.setHeader("Content-Type", mytype); + } + } + return this.setHeader( + "Content-Type", + `${this.contentType}; charset=${this.encoding}` + ); + } + + setFileMimeType(type: string, encoding?: BufferEncoding) { let myType = this.getMimeType(type); if (!myType) { this.log(`Content-Type not valid !!! : ${type}`, "WARNING"); @@ -113,6 +166,19 @@ class HttpResponse { ); } + setContentTypeByExtension(extention: string) { + const ismime = mime.contentType(extention); + if (ismime) { + this.contentType = ismime; + let charset = mime.charset(this.contentType); + if (charset) { + this.encoding = charset as BufferEncoding; + } + return this.setHeader("Content-Type", ismime); + } + this.log(`setContentTypeByExtension: ${extention} not found`, "WARNING"); + } + getMimeType(filenameOrExt: string): string | false { return mime.lookup(filenameOrExt); } diff --git a/src/packages/@nodefony/http/nodefony/src/context/http2/Response.ts b/src/packages/@nodefony/http/nodefony/src/context/http2/Response.ts index 84bc466..00c9e05 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/http2/Response.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/http2/Response.ts @@ -37,7 +37,11 @@ class Http2Response extends HttpResponse { if (this.context.method === "HEAD" || this.context.contentLength) { this.setHeader("Content-Length", this.getLength()); } - this.headers = extend(this.getHeaders(), headers); + this.headers = extend( + { "X-Status-Message": this.statusMessage }, + this.getHeaders(), + headers + ); if (this.statusCode) { if (typeof this.statusCode === "string") { this.statusCode = parseInt(this.statusCode, 10); @@ -90,9 +94,9 @@ class Http2Response extends HttpResponse { encoding || this.encoding, (error) => { if (error) { - reject(false); + return reject(error); } - resolve(true); + return resolve(true); } ); } @@ -121,9 +125,18 @@ class Http2Response extends HttpResponse { }); } - override getStatusMessage(code?: string | number | undefined): string { - // return this.statusMessage || http.STATUS_CODES[this.statusCode]; - return ""; + getStatusMessage(code?: number | string): string { + if (code) { + if (this.response) { + return (http.STATUS_CODES[code] as string) || this.statusMessage; + } + } + if (this.response) { + return ( + this.statusMessage || (http.STATUS_CODES[this.statusCode] as string) + ); + } + return this.statusMessage || (http.STATUS_CODES[this.statusCode] as string); } override getStatus(): { code: number; message: string } { diff --git a/src/packages/@nodefony/http/nodefony/src/context/websocket/Response.ts b/src/packages/@nodefony/http/nodefony/src/context/websocket/Response.ts index 71e29c4..422dfcd 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/websocket/Response.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/websocket/Response.ts @@ -1,6 +1,11 @@ +import Cookie from "../../cookies/cookie"; class WebsocketResponse { statusCode: number = 1000; constructor() {} + + addCookie(cookie: Cookie) {} + + setCookie(cookie: Cookie) {} } export default WebsocketResponse; diff --git a/src/packages/@nodefony/http/nodefony/src/context/websocket/WebsocketContext.ts b/src/packages/@nodefony/http/nodefony/src/context/websocket/WebsocketContext.ts index 53efd33..9b2e95a 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/websocket/WebsocketContext.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/websocket/WebsocketContext.ts @@ -34,6 +34,18 @@ class WebsocketContext extends Context { async connect(): Promise {} async handle(): Promise {} + + getRemoteAddress(): string | null { + return this.request?.remoteAddress; + } + + getHost(): string | undefined { + return this.request.httpRequest.headers.host; + } + + getUserAgent(): string { + return ""; + } } export default WebsocketContext; diff --git a/src/packages/@nodefony/http/nodefony/src/cookies/cookie.ts b/src/packages/@nodefony/http/nodefony/src/cookies/cookie.ts index 17d9101..e2e10d6 100644 --- a/src/packages/@nodefony/http/nodefony/src/cookies/cookie.ts +++ b/src/packages/@nodefony/http/nodefony/src/cookies/cookie.ts @@ -10,7 +10,7 @@ import websocket from "websocket"; import { ContextType } from "../../service/http-kernel"; -type SameSiteType = boolean | "none" | "lax" | "strict"; +type SameSiteType = boolean | "none" | "Lax" | "Strict"; type PriorityType = "High" | "Medium" | "Low" | undefined; declare module "websocket" { @@ -48,8 +48,8 @@ const cookieDefaultSettings: CookieOptionsType = { maxAge: 0, // 24*60*60, path: "/", domain: undefined, - secure: false, - sameSite: "none", + secure: true, + sameSite: "Lax", httpOnly: true, signed: false, secret: "!nodefony.secret!", @@ -390,4 +390,4 @@ class Cookie { } export default Cookie; -export { cookiesParser, parser, getRequestcookies }; +export { cookiesParser, parser, parserWs, getRequestcookies }; diff --git a/src/packages/@nodefony/http/nodefony/src/errors/httpError.ts b/src/packages/@nodefony/http/nodefony/src/errors/httpError.ts index ea8729a..fa9a212 100644 --- a/src/packages/@nodefony/http/nodefony/src/errors/httpError.ts +++ b/src/packages/@nodefony/http/nodefony/src/errors/httpError.ts @@ -4,21 +4,29 @@ import { ContextType } from "../../service/http-kernel"; import { HttpRequestType, HttpRsponseType } from "../context/http/HttpContext"; class HttpError extends NodefonyError { - context: ContextType; - response: HttpRsponseType; - request: HttpRequestType; - url: string; + context?: ContextType; + response?: HttpRsponseType; + request?: HttpRequestType; + url?: string; constructor( - message: string | NodefonyError | Error, - code: number, - context: ContextType + message?: string | NodefonyError | Error, + code?: number, + context?: ContextType ) { - super(message, code || context.response?.statusCode); - + super(message, code); this.context = context; - this.response = context.response as HttpRsponseType; - this.request = context.request as HttpRequestType; - this.url = this.context.url; + this.response = context?.response as HttpRsponseType; + this.request = context?.request as HttpRequestType; + this.url = this.context?.url; + if (this.response && code) { + this.response.statusCode = code; + } + if (!this.message) { + this.message = + (context?.response as HttpRsponseType)?.body?.toString() || + (context?.response as HttpRsponseType)?.statusMessage || + (context?.response as HttpRsponseType)?.getStatusMessage(); + } } override toString(): string { @@ -26,10 +34,9 @@ class HttpError extends NodefonyError { return `${clc.red(this.message)} ${clc.blue("Name :")} ${this.name} ${clc.blue("Type :")} ${this.errorType} - ${clc.blue("Url :")} ${this.url} ${clc.green("Controller :")} ${this.controller} ${clc.green("Action :")} ${this.action} - ${clc.blue("clientRequest :")} ${this.requestUrl} + ${clc.blue("Url :")} ${this.url} ${clc.red("Code :")} ${this.code} ${clc.red("Message :")} ${this.message} ${clc.red("Response :")} ${this.jsonResponse} @@ -38,7 +45,7 @@ class HttpError extends NodefonyError { return `${clc.red(this.message)} ${clc.blue("Name :")} ${this.name} ${clc.blue("Type :")} ${this.errorType} - ${clc.blue("clientRequest :")} ${this.requestUrl} + ${clc.red("Code :")} ${this.code} ${clc.red("Message :")} ${this.message} ${clc.red("Response :")} ${this.jsonResponse} @@ -174,6 +181,16 @@ export default HttpError; // } // } +// parserResponse() { +// const json = this.response?.toJSON(); +// if (json) { +// this.requestUrl = json.request.uri.href; +// this.code = json.statusCode; +// this.jsonResponse = JSON.stringify(json, null, " "); +// } +// this.parseMessage(json.body); +// } + // parserResponse() { // const json = this.response.toJSON(); // if (json) { diff --git a/src/packages/@nodefony/http/nodefony/src/session/session.ts b/src/packages/@nodefony/http/nodefony/src/session/session.ts index 99a4c54..27f60b8 100644 --- a/src/packages/@nodefony/http/nodefony/src/session/session.ts +++ b/src/packages/@nodefony/http/nodefony/src/session/session.ts @@ -1,3 +1,694 @@ -class Session {} +import nodefony, { + extend, + Container, + Severity, + Msgid, + Message, + Pdu, + ProtoService, + ProtoParameters, +} from "nodefony"; +import sessionService, { + sessionStrategyType, + FlashBagSessionType, + MetaBagSessionType, + SerializeSessionType, + sessionStorageInterface, +} from "../../service/sessions/sessions-service"; +import HttpKernel, { ContextType } from "../../service/http-kernel"; +import { + createHash, + createCipheriv, + createDecipheriv, + randomBytes, +} from "node:crypto"; +import Cookie, { CookieOptionsType } from "../cookies/cookie"; +import HttpContext from "../context/http/HttpContext"; +import WebsocketContext from "../context/websocket/WebsocketContext"; +import HttpResquest from "../context/http/Request"; + +type algorithmSessionType = { + algorithm: string; + password: string; + iv: string; +}; + +export type OptionsSessionType = { + name?: string; + encrypt?: algorithmSessionType; + use_only_cookies?: boolean; + use_trans_sid?: boolean; + use_cookies?: boolean; + hash_function?: string; + cookie?: CookieOptionsType; + use_strict_mode: boolean; + referer_check: boolean; +}; +type StatusSessionType = "none" | "active" | "disabled"; + +const defaultSessionOptions: OptionsSessionType = { + name: "nodefony", + use_only_cookies: true, + use_trans_sid: true, + use_cookies: true, + hash_function: "sha1", + use_strict_mode: true, + referer_check: false, +}; + +class Session extends Container { + id: string = ""; + name: string = ""; + status: StatusSessionType = "none"; + storage: sessionStorageInterface; + manager: sessionService; + saved: boolean = false; + migrated: boolean = false; + contextSession: string = "default"; + context?: ContextType; + created?: Date; + updated?: Date; + options: OptionsSessionType; + cookieSession: Cookie | null | undefined = null; + applyTranId: boolean = false; + lifetime?: number; + flashBag: FlashBagSessionType = {}; + user?: string; + strategy: sessionStrategyType; + strategyNone: boolean = false; + constructor( + name: string, + options: OptionsSessionType, + manager: sessionService + ) { + super(); + this.options = extend({}, defaultSessionOptions, options); + this.manager = manager; + this.storage = this.manager.storage; + if (!this.storage) { + this.status = "disabled"; + } + this.setName(name); + this.strategy = this.manager.sessionStrategy; + } + + log(pci: any, severity?: Severity, msgid?: Msgid, msg?: Message): Pdu { + if (!msgid) { + msgid = `SESSION ${this.name}`; + } + return this.manager.log(pci, severity, msgid, msg); + } + + async start(context: ContextType, contextSession: string): Promise { + this.context = context; + if (!contextSession) { + // eslint-disable-next-line prefer-destructuring + contextSession = this.contextSession; + } + if (this.options.use_only_cookies) { + this.applyTranId = true; + } else { + this.applyTranId = this.options.use_trans_sid as boolean; + } + try { + const ret = this.checkStatus(); + switch (ret) { + case false: + return Promise.resolve(this); + case "restart": + return this.start(context, contextSession).catch((e) => { + throw e; + }); + default: + return this.getSession(contextSession).catch((e) => { + throw e; + }); + } + } catch (e) { + this.log(e, "ERROR"); + throw e; + } + } + + create( + lifetime: number, + id?: string, + settingsCookie: CookieOptionsType = {} + ): this { + this.id = id || this.setId(); + const defaultSetting = extend({}, this.options.cookie); + settingsCookie = nodefony.extend(defaultSetting, settingsCookie); + this.log(`NEW SESSION CREATE : ${this.id}`, "DEBUG"); + try { + this.cookieSession = this.setCookieSession(lifetime, settingsCookie); + this.setMetasSession(settingsCookie); + this.status = "active"; + return this; + } catch (e) { + this.log(e, "ERROR"); + throw new Error("Request can't create cookieSession"); + // this.log(`,"WARNING") + } + } + + async checkChangeContext(contextSession: string): Promise { + // change context session + if (contextSession && this.contextSession !== contextSession) { + this.log( + `SESSION CONTEXT CHANGE : ${this.contextSession} ==> ${contextSession}` + ); + switch (this.strategy) { + case "migrate": + return this.storage + .start(this.id, this.contextSession) + .then(async (result: SerializeSessionType) => { + this.deSerialize(result); + if ( + !this.isValidSession( + result, + this.context as HttpContext | WebsocketContext + ) + ) { + this.log( + `INVALID SESSION ==> ${this.name} : ${this.id}`, + "WARNING" + ); + await this.destroy().catch((e) => { + throw e; + }); + this.contextSession = contextSession; + return this.create(this.lifetime as number); + } + await this.removeSession(); + this.log( + `STRATEGY MIGRATE SESSION ==> ${this.name} : ${this.id}`, + "DEBUG" + ); + this.migrated = true; + this.contextSession = contextSession; + return this.create(this.lifetime as number); + }) + .catch((error: Error) => { + throw error; + }); + case "invalidate": + this.log( + `STRATEGY INVALIDATE SESSION ==> ${this.name} : ${this.id}`, + "DEBUG" + ); + await this.destroy().catch((e) => { + throw e; + }); + this.contextSession = contextSession; + return new Promise((resolve, reject) => { + try { + resolve(this.create(this.lifetime as number)); + } catch (e) { + reject(e); + } + }); + case "none": + this.strategyNone = true; + break; + default: + } + if (!this.strategyNone) { + return this; + } + } + return this.storage + .start(this.id, this.contextSession) + .then(async (result: SerializeSessionType) => { + try { + if (result && Object.keys(result).length) { + this.deSerialize(result); + if ( + !this.isValidSession( + result, + this.context as HttpContext | WebsocketContext + ) + ) { + this.log( + `SESSION ==> ${this.name} : ${this.id} session invalid `, + "ERROR" + ); + await this.invalidate().catch((e) => { + throw e; + }); + } + } else if (this.options.use_strict_mode) { + if (!this.strategyNone) { + this.log( + `SESSION ==> ${this.name} : ${this.id} use_strict_mode `, + "ERROR" + ); + await this.invalidate().catch((e) => { + throw e; + }); + } + } + this.status = "active"; + return this; + } catch (e) { + throw e; + } + }) + .catch(async (error: Error) => { + if (error) { + try { + this.log(`SESSION ==> ${this.name} : ${this.id} ${error}`, "ERROR"); + if (!this.strategyNone) { + await this.invalidate().catch((e: Error) => { + throw e; + }); + } + throw error; + } catch (e) { + throw error; + } + } + throw error; + }); + } + + async invalidate( + lifetime: number = this.lifetime as number, + id?: string, + settingsCookie: CookieOptionsType = {} + ) { + this.log(`INVALIDATE SESSION ==>${this.name} : ${this.id}`, "DEBUG"); + this.saved = true; + return await this.destroy(!!this.cookieSession) + .then(() => { + this.saved = false; + return this.create(lifetime, id, settingsCookie); + }) + .catch((e: Error) => { + throw e; + }); + } + + setId(): string { + let ip = ""; + try { + ip = ( + this.context as HttpContext | WebsocketContext + )?.getRemoteAddress() as string; + } catch (e) { + this.log(e, "DEBUG"); + } + const date = new Date().getTime(); + // eslint-disable-next-line no-mixed-operators + const concat = ip + date + this.randomValueHex(16) + Math.random() * 10; + let hash = null; + switch (this.options.hash_function) { + case "md5": + hash = createHash("md5"); + break; + case "sha1": + hash = createHash("sha1"); + break; + default: + hash = createHash("md5"); + } + const res = hash.update(concat).digest("hex"); + return this.encrypt(`${res}:${this.contextSession}`); + } + + getId(value: string) { + const res = this.decrypt(value); + // eslint-disable-next-line prefer-destructuring + this.contextSession = res.split(":")[1]; + return value; + } + + async getSession(contextSession: string): Promise { + if (this.options.use_cookies) { + if (this.context?.cookieSession) { + this.id = this.getId(this.context.cookieSession.value); + this.cookieSession = this.context.cookieSession; + } + this.applyTranId = false; + } + if (!this.options.use_only_cookies && !this.id) { + const request = this.context?.request as HttpResquest; + if (request && this.name in request.query) { + this.id = this.getId(request.query[this.name]); + } + } + if (this.id) { + return this.checkChangeContext(contextSession).catch((e) => { + throw e; + }); + } + try { + this.clear(); + return this.create(this.lifetime as number); + } catch (e) { + throw e; + } + } + + isValidSession(data: SerializeSessionType, context: ContextType): boolean { + if (this.options.referer_check) { + try { + return this.checkSecureReferer(context); + } catch (e) { + this.log( + `SESSION REFERER ERROR SESSION ==> ${this.name} : ${this.id}`, + "WARNING" + ); + return false; + } + } + // console.log( this.updated , new Date(this.updated) ) + const lastUsed = new Date(this.updated as Date).getTime(); + // let lastUsed = new Date(this.getMetaBag("lastUsed")).getTime(); + const now = new Date().getTime(); + if (this.lifetime === 0) { + // if ( lastUsed && lastUsed + ( this.settings.gc_maxlifetime * 1000 ) < now ){ + // this.manager.log("SESSION INVALIDE gc_maxlifetime ==> " + this.name + " : "+ this.id, "WARNING"); + // return false ; + // } + return true; + } + // eslint-disable-next-line no-mixed-operators + if (lastUsed && lastUsed + (this.lifetime as number) * 1000 < now) { + this.log( + `SESSION INVALIDE lifetime ==> ${this.name} : ${this.id}`, + "WARNING" + ); + return false; + } + return true; + } + + checkSecureReferer(context: ContextType): boolean { + let host = (context as HttpContext | WebsocketContext).getHost(); + const meta = this.getMetaBag("host"); + if (host === meta) { + return true; + } + this.log( + `SESSION START WARNING REFERRER NOT SAME, HOST : ${host} ,META STORAGE :${meta}`, + "WARNING" + ); + throw { + meta, + host, + }; + } + + async delete(cookieDelete?: boolean): Promise { + return this.destroy(cookieDelete); + } + + async destroy(cookieDelete?: boolean): Promise { + this.clear(); + return this.removeSession(cookieDelete); + } + + async removeSession(cookieDelete: boolean = false): Promise { + if (this.saved === true) { + return this.storage + .destroy(this.id, this.contextSession) + .then(() => { + if (cookieDelete) { + this.deleteCookieSession(); + } + this.saved = true; + return true; + }) + .catch((e: Error) => { + this.log(e, "ERROR"); + throw e; + }); + } + if (cookieDelete) { + this.deleteCookieSession(); + } + return Promise.resolve(true); + } + + setCookieSession(leftTime: number, options: CookieOptionsType = {}) { + if (this.context && this.context.response) { + // let settings = null; + const defaultsettings = nodefony.extend({}, this.options.cookie); + options = nodefony.extend(defaultsettings, options); + if (leftTime) { + options.maxAge = leftTime; + } + const cookie = new Cookie(this.name, this.id, options); + this.context.response.addCookie(cookie); + this.cookieSession = cookie; + this.context.cookieSession = cookie; + return cookie; + } + return null; + } + + deleteCookieSession() { + if (this.context && this.context.response) { + let cookie: Cookie = this.cookieSession as Cookie; + if (cookie) { + cookie.expires = new Date(0) as Date; + } else { + // eslint-disable-next-line new-cap + cookie = new Cookie(this.name, "", { + expires: new Date(0), + // path: "/" + }); + } + this.context.response.setCookie(cookie as Cookie); + this.cookieSession = null; + this.context.cookieSession = null; + return cookie; + } + return this.cookieSession; + } + + clear() { + super.reset(); + this.clearFlashBags(); + } + + getName() { + return this.name; + } + + encrypt(text: string): string { + //console.log("encrypt", text); + const cipher = createCipheriv( + "aes-256-ctr", + this.manager.secret as Buffer, + this.manager.iv as Buffer + ); + let encrypted = cipher.update(text, "utf8", "hex"); + encrypted += cipher.final("hex"); + return encrypted; + } + + decrypt(text: string): string { + //console.log("decrypt", text); + const decipher = createDecipheriv( + "aes-256-ctr", + this.manager.secret as Buffer, + this.manager.iv as Buffer + ); + + let decrypted = decipher.update(text, "hex", "utf8"); + //console.log(decipher, decrypted); + decrypted += decipher.final("utf8"); + //console.log(decrypted.toString()); + return decrypted; + } + + checkStatus(): "restart" | boolean { + switch (this.status) { + case "active": + this.log( + `SESSION ALLREADY STARTED ==> ${this.name} : ${this.id}`, + "WARNING" + ); + return false; + case "disabled": + try { + this.storage = this.manager.initializeStorage(); + if (this.storage) { + this.status = "none"; + return "restart"; + } + } catch (e) { + this.log("SESSION STORAGE HANDLER NOT FOUND ", "ERROR"); + throw new Error("SESSION STORAGE HANDLER NOT FOUND "); + } + break; + default: + return true; + } + return true; + } + + setName(name: string) { + this.name = name || (this.options.name as string); + } + + async save(user: string, contextSession: string) { + return this.storage + .write(this.id, this.serialize(user), contextSession) + .then((session: any) => { + this.created = session.createdAt; + this.updated = session.updatedAt; + if (!this.context) { + throw new Error("SAVE SESSION ERROR context already deleted "); + } else { + this.saved = true; + if (this.context) { + this.context.fire("onSaveSession", this); + } + return this; + } + }) + .catch((error: Error) => { + // console.trace(error); + // this.log(error, "ERROR"); + this.saved = false; + throw error; + }); + } + + setMetasSession(cookieSetting: CookieOptionsType = {}): void { + // let time = new Date(); + let ua = null; + this.setMetaBag( + "lifetime", + cookieSetting.maxAge || this.options?.cookie?.maxAge + ); + this.setMetaBag("context", this.contextSession || null); + const type = (this.context as HttpContext | WebsocketContext) + ?.type as string; + this.setMetaBag("request", type); + // this.setMetaBag("created", time); + try { + const ip = ( + this.context as HttpContext | WebsocketContext + )?.getRemoteAddress() as string; + const host = ( + this.context as HttpContext | WebsocketContext + )?.getHost() as string; + const agent = ( + this.context as HttpContext | WebsocketContext + )?.getUserAgent() as string; + + this.setMetaBag("remoteAddress", ip); + this.setMetaBag("host", host); + if (agent) { + this.setMetaBag("user_agent", agent); + } else { + this.setMetaBag("user_agent", "Not Defined"); + } + } catch (e) { + this.log(e, "DEBUG"); + } + } + + attributes() { + return this.protoService.prototype; + } + + getAttributes() { + return this.attributes(); + } + + metaBag(): MetaBagSessionType { + return this.protoParameters.prototype; + } + + getMetas(): MetaBagSessionType { + return this.metaBag(); + } + + setMetaBag(key: string, value: any) { + return this.setParameters(key, value); + } + + getMetaBag(key: string): any { + return this.getParameters(key); + } + + clearFlashBags() { + this.flashBag = {}; + } + + getFlashBag(key: string): any { + // this.log("GET FlashBag : " + key ,"WARNING") + const res = this.flashBag[key]; + if (res) { + this.log(`Delete FlashBag : ${key}`, "DEBUG"); + delete this.flashBag[key]; + return res; + } + return null; + } + + setFlashBag(key: string, value: any): any { + if (!key) { + throw new Error(`FlashBag key must be define : ${key}`); + } + if (!value) { + this.log(`ADD FlashBag : ${key} value not defined `, "WARNING"); + } else { + this.log(`ADD FlashBag : ${key}`, "DEBUG"); + } + this.flashBag[key] = value; + return value; + } + + flashBags(): FlashBagSessionType { + return this.flashBag; + } + + clearFlashBag(key: string) { + if (!key) { + throw new Error(`clearFlashBag key must be define : ${key}`); + } + if (this.flashBag[key]) { + delete this.flashBag[key]; + } + } + + serialize(user: string): SerializeSessionType { + const obj = { + Attributes: this.services as ProtoService, + metaBag: this.parameters as ProtoParameters, + flashBag: this.flashBag, + user, + }; + return obj; + } + + deSerialize(obj: Record): void { + // var obj = JSON.parse(data); + for (const attr in obj.Attributes) { + this.set(attr, obj.Attributes[attr]); + } + for (const meta in obj.metaBag) { + // console.log(meta + " : " + obj.metaBag[meta]) + this.setMetaBag(meta, obj.metaBag[meta]); + } + for (const flash in obj.flashBag) { + this.setFlashBag(flash, obj.flashBag[flash]); + } + this.created = obj.created; + this.updated = obj.updated; + this.user = obj.user; + } + + randomValueHex(len: number) { + return randomBytes(Math.ceil(len / 2)) + .toString("hex") // convert to hexadecimal format + .slice(0, len); // return required number of characters + } +} export default Session; diff --git a/src/packages/@nodefony/http/nodefony/src/session/storage/FileSessionStorage.ts b/src/packages/@nodefony/http/nodefony/src/session/storage/FileSessionStorage.ts new file mode 100644 index 0000000..9477778 --- /dev/null +++ b/src/packages/@nodefony/http/nodefony/src/session/storage/FileSessionStorage.ts @@ -0,0 +1,219 @@ +import { mkdirp } from "mkdirp"; +import fs from "node:fs"; +import nodefony, { + extend, + Service, + Kernel, + Container, + Event, + Module, + FamilyType, + FileClass, + Finder, + Result, +} from "nodefony"; +import sessionService, { + sessionStorageInterface, + SerializeSessionType, +} from "../../../service/sessions/sessions-service"; +import HttpKernel, { + ProtocolType, + ServerType, + ContextType, +} from "../../../service/http-kernel"; + +const finderGC = function ( + this: FileSessionStorage, + path: string, + msMaxlifetime: number, + context: string +) { + let nbSessionsDelete = 0; + return new Finder().in(path, { + onFile: (file: FileClass) => { + const mtime = new Date(file.stats.mtime).getTime(); + if (mtime + msMaxlifetime < new Date().getTime()) { + file.unlink(); + this.manager.log( + `FILES SESSIONS STORAGE GARBADGE COLLECTOR SESSION context : ${context} ID : ${file.name} DELETED` + ); + nbSessionsDelete++; + } + }, + onFinish: (/* error, result*/) => { + this.manager.log( + `FILES SESSIONS STORAGE context : ${context || "default"} GARBADGE COLLECTOR ==> ${nbSessionsDelete} DELETED` + ); + }, + }); +}; + +class FileSessionStorage implements sessionStorageInterface { + manager: sessionService; + path: string; + gc_maxlifetime: number; + + contextSessions: string[]; + constructor(manager: sessionService) { + this.manager = manager; + this.path = manager.options.save_path; + this.gc_maxlifetime = manager.options.gc_maxlifetime; + this.contextSessions = []; + } + + async start( + id: string, + contextSession: string + ): Promise { + let fileSession: FileClass; + let Path: string = ""; + if (contextSession) { + Path = `${this.path}/${contextSession}/${id}`; + } else { + Path = `${this.path}/default/${id}`; + } + try { + fileSession = new FileClass(Path); + } catch (e) { + return {} as SerializeSessionType; + } + try { + return this.read(fileSession.path as string); + } catch (e) { + throw e; + } + } + + async open(contextSession: string): Promise { + return new Promise((resolve, reject) => { + let Path = null; + if (contextSession) { + Path = `${this.path}/${contextSession}`; + this.contextSessions.push(contextSession); + } else { + Path = this.path; + } + const res = fs.existsSync(Path); + let result: Result; + if (!res) { + this.manager.log(`create directory context sessions ${Path}`); + try { + mkdirp.sync(Path); + } catch (e) { + return reject(e); + } + } else { + this.gc(this.gc_maxlifetime, contextSession); + return new Finder().in(Path, { + recurse: false, + onFinish: (result: Result) => { + let total: number = 0; + if (result[0]) { + total = result[0].childrens.length; + } + this.manager.log( + `CONTEXT ${contextSession ? contextSession : "GLOBAL"} SESSIONS STORAGE ==> ${this.manager.options.handler.toUpperCase()} COUNT SESSIONS : ${total}` + ); + + return resolve(total); + }, + }); + } + return resolve(0); + }); + } + + close(): boolean { + this.gc(this.gc_maxlifetime); + return true; + } + + async destroy(id: string, contextSession: string): Promise { + let fileDestroy: FileClass; + let Path = null; + if (contextSession) { + Path = `${this.path}/${contextSession}/${id}`; + } else { + Path = `${this.path}/default/${id}`; + } + try { + fileDestroy = new FileClass(Path); + } catch (e) { + this.manager.log(`STORAGE FILE :${Path}`, "DEBUG"); + return true; + } + return new Promise((resolve, reject) => { + try { + this.manager.log( + `FILES SESSIONS STORAGE DESTROY SESSION context : ${contextSession} ID : ${fileDestroy.name} DELETED` + ); + fileDestroy.unlink(); + return resolve(true); + } catch (e) { + return reject(id); + } + }); + } + + async gc(maxlifetime?: number, contextSession?: string): Promise { + const msMaxlifetime = (maxlifetime || this.gc_maxlifetime) * 1000; + if (contextSession) { + const Path = `${this.path}/${contextSession}`; + finderGC.call(this, Path, msMaxlifetime, contextSession); + } else if (this.contextSessions.length) { + for (let i = 0; i < this.contextSessions.length; i++) { + finderGC.call( + this, + `${this.path}/${this.contextSessions[i]}`, + msMaxlifetime, + this.contextSessions[i] + ); + } + } + } + + read(file: string): Promise { + return new Promise((resolve, reject) => { + // let id = file.name; + try { + fs.readFile(file, "utf8", (err, data) => { + if (err) { + return reject(err); + } + return resolve(JSON.parse(data) as SerializeSessionType); + }); + } catch (e) { + this.manager.log(`FILES SESSIONS STORAGE READ ==> ${e}`, "ERROR"); + return reject(e); + } + }); + } + + write( + fileName: string, + serialize: SerializeSessionType, + contextSession: string + ): Promise { + let Path: string = ""; + if (contextSession) { + Path = `${this.path}/${contextSession}/${fileName}`; + } else { + Path = `${this.path}/default/${fileName}`; + } + return new Promise((resolve, reject) => { + try { + fs.writeFile(Path, JSON.stringify(serialize), "utf8", (err) => { + if (err) { + return reject(err); + } + return resolve(serialize); + }); + } catch (e) { + this.manager.log(`FILES SESSIONS STORAGE : ${e}`, "ERROR"); + return reject(e); + } + }); + } +} + +export default FileSessionStorage; diff --git a/src/packages/@nodefony/http/package-lock.json b/src/packages/@nodefony/http/package-lock.json index ff6d0fb..55587f3 100644 --- a/src/packages/@nodefony/http/package-lock.json +++ b/src/packages/@nodefony/http/package-lock.json @@ -42,8 +42,8 @@ "@types/uuid": "9.0.8", "@types/websocket": "1.0.10", "@types/xml2js": "0.4.14", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "rollup-plugin-copy": "3.5.0", @@ -818,16 +818,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", - "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/type-utils": "6.20.0", - "@typescript-eslint/utils": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -853,15 +853,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", - "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -881,13 +881,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", - "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -898,13 +898,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", - "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -925,9 +925,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -938,13 +938,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -966,17 +966,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", - "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -991,12 +991,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", - "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/src/packages/@nodefony/http/package.json b/src/packages/@nodefony/http/package.json index 5b86aef..64564a2 100644 --- a/src/packages/@nodefony/http/package.json +++ b/src/packages/@nodefony/http/package.json @@ -54,8 +54,8 @@ "@types/uuid": "9.0.8", "@types/websocket": "1.0.10", "@types/xml2js": "0.4.14", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "rollup-plugin-copy": "3.5.0", diff --git a/src/packages/@nodefony/http/rollup.config.ts b/src/packages/@nodefony/http/rollup.config.ts index 32effb8..dc83bca 100644 --- a/src/packages/@nodefony/http/rollup.config.ts +++ b/src/packages/@nodefony/http/rollup.config.ts @@ -40,8 +40,6 @@ const sharedNodeOptions = defineConfig({ //chunkFileNames: "node/chunks/dep-[hash].js", exports: "auto", format: "esm", - externalLiveBindings: false, - freeze: false, }, onwarn(warning, warn) { if (warning.message.includes("Circular dependency")) { @@ -88,8 +86,8 @@ function createNodeConfig(isProduction: boolean): RollupOptions { output: { ...sharedNodeOptions.output, sourcemap: !isProduction, - //preserveModules: true, - //preserveModulesRoot: "src", + preserveModules: !isProduction, + preserveModulesRoot: "nodefony", }, external, plugins: [...createNodePlugins(isProduction, true, "dist/types")], diff --git a/src/packages/@nodefony/security/package.json b/src/packages/@nodefony/security/package.json index e12f9ca..1513270 100644 --- a/src/packages/@nodefony/security/package.json +++ b/src/packages/@nodefony/security/package.json @@ -21,9 +21,6 @@ ], "repository": {}, "dependencies": { - "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", "bcrypt": "5.1.1", "csrf": "^3.1.0", "jsonwebtoken": "9.0.2", @@ -44,8 +41,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "rollup-plugin-copy": "3.5.0", diff --git a/src/packages/@nodefony/sequelize/package.json b/src/packages/@nodefony/sequelize/package.json index 03eca39..f435c8d 100755 --- a/src/packages/@nodefony/sequelize/package.json +++ b/src/packages/@nodefony/sequelize/package.json @@ -31,8 +31,8 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "11.1.6", "@types/node": "20.11.16", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "rimraf": "5.0.5", "rollup": "4.9.6", "sequelize-cli": "6.6.2",