diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json
index ba7bd845261fe22f359c5c422f92c34a7d1d9420..f989951efc4753f32445fed2d24dd47285be787e 100644
--- a/NodeApp/package-lock.json
+++ b/NodeApp/package-lock.json
@@ -9,7 +9,9 @@
             "version": "3.6.0",
             "license": "AGPLv3",
             "dependencies": {
-                "@gitbeaker/rest": "^39.34.3",
+                "@gitbeaker/core": "^40.0.1",
+                "@gitbeaker/requester-utils": "^40.0.1",
+                "@gitbeaker/rest": "^40.0.1",
                 "appdata-path": "^1.0.0",
                 "axios": "^1.6.5",
                 "boxen": "^5.1.2",
@@ -28,6 +30,7 @@
                 "semver": "^7.5.4",
                 "tar-stream": "^3.1.6",
                 "winston": "^3.11.0",
+                "winston-transport": "^4.7.0",
                 "yaml": "^2.3.4",
                 "zod": "^3.22.4",
                 "zod-validation-error": "^3.0.0"
@@ -253,9 +256,9 @@
             }
         },
         "node_modules/@eslint/js": {
-            "version": "8.56.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
-            "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+            "version": "8.57.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+            "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
             "dev": true,
             "peer": true,
             "engines": {
@@ -263,11 +266,11 @@
             }
         },
         "node_modules/@gitbeaker/core": {
-            "version": "39.34.3",
-            "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-39.34.3.tgz",
-            "integrity": "sha512-/3qBXme2MjO38QU2F/MYGon9a4wHKrgtwNzdHHdjpbYJ2/wOGNgbEWSZcibcFkiWVgAjbPXdYqC5sY8hcwGO1w==",
+            "version": "40.0.1",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-40.0.1.tgz",
+            "integrity": "sha512-Zh2eVUgy2kYVnp7Db4gWoFqFbjgsnm2FvBEERbH3UM3cOA/iMqM+tw/of+Qk4yO+gv6tGZ9f4nF7+vK0tQFmDA==",
             "dependencies": {
-                "@gitbeaker/requester-utils": "^39.34.3",
+                "@gitbeaker/requester-utils": "^40.0.1",
                 "qs": "^6.11.2",
                 "xcase": "^2.0.1"
             },
@@ -276,9 +279,9 @@
             }
         },
         "node_modules/@gitbeaker/requester-utils": {
-            "version": "39.34.3",
-            "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-39.34.3.tgz",
-            "integrity": "sha512-nMnTkTo4UixHPwPYsYIjp8UdKrmSw3TjvRESexliAeNNq4/LVeyVUyRqBUa1ZI8MXt1nPPnPX3wh8s7rqlm7uA==",
+            "version": "40.0.1",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-40.0.1.tgz",
+            "integrity": "sha512-cn6fltKuQ3TbthoMTg+JsKQfozqGcRcz1jT9Nqzr4gpHWgjdQ/nr5JpjwzKABQNVL2JH3UJWr6Eji60CFZDZ6Q==",
             "dependencies": {
                 "picomatch-browser": "^2.2.6",
                 "qs": "^6.11.2",
@@ -290,12 +293,12 @@
             }
         },
         "node_modules/@gitbeaker/rest": {
-            "version": "39.34.3",
-            "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-39.34.3.tgz",
-            "integrity": "sha512-SuceThS6WhJtqNNcKmW8j0yUU7aXA4k5a29OWcd6bn7peQ3MXlIpbfvLLRnmuUaYUuxHLnUzZhAfuxaNf4DVtQ==",
+            "version": "40.0.1",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-40.0.1.tgz",
+            "integrity": "sha512-JEd9WNuzgur7gLiJPMWPYKaWe5uX1ic8CGKR1fMtBityFZ2xyZkTZ+LG0nqWTV1MyiowYnJ1swTh8Yff+kLsKA==",
             "dependencies": {
-                "@gitbeaker/core": "^39.34.3",
-                "@gitbeaker/requester-utils": "^39.34.3"
+                "@gitbeaker/core": "^40.0.1",
+                "@gitbeaker/requester-utils": "^40.0.1"
             },
             "engines": {
                 "node": ">=18.0.0"
@@ -362,14 +365,14 @@
             "peer": true
         },
         "node_modules/@jridgewell/gen-mapping": {
-            "version": "0.3.3",
-            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-            "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+            "version": "0.3.5",
+            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+            "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
             "dev": true,
             "dependencies": {
-                "@jridgewell/set-array": "^1.0.1",
+                "@jridgewell/set-array": "^1.2.1",
                 "@jridgewell/sourcemap-codec": "^1.4.10",
-                "@jridgewell/trace-mapping": "^0.3.9"
+                "@jridgewell/trace-mapping": "^0.3.24"
             },
             "engines": {
                 "node": ">=6.0.0"
@@ -385,9 +388,9 @@
             }
         },
         "node_modules/@jridgewell/set-array": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-            "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+            "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
             "dev": true,
             "engines": {
                 "node": ">=6.0.0"
@@ -400,9 +403,9 @@
             "dev": true
         },
         "node_modules/@jridgewell/trace-mapping": {
-            "version": "0.3.22",
-            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
-            "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
+            "version": "0.3.25",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+            "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
             "dev": true,
             "dependencies": {
                 "@jridgewell/resolve-uri": "^3.1.0",
@@ -904,18 +907,18 @@
             }
         },
         "node_modules/@types/node": {
-            "version": "18.19.17",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.17.tgz",
-            "integrity": "sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==",
+            "version": "18.19.24",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.24.tgz",
+            "integrity": "sha512-eghAz3gnbQbvnHqB+mgB2ZR3aH6RhdEmHGS48BnV75KceQPHqabkxKI0BbUSsqhqy2Ddhc2xD/VAR9ySZd57Lw==",
             "dev": true,
             "dependencies": {
                 "undici-types": "~5.26.4"
             }
         },
         "node_modules/@types/semver": {
-            "version": "7.5.7",
-            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
-            "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
+            "version": "7.5.8",
+            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+            "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
             "dev": true
         },
         "node_modules/@types/tar-stream": {
@@ -1317,11 +1320,11 @@
             }
         },
         "node_modules/axios": {
-            "version": "1.6.7",
-            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
-            "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+            "version": "1.6.8",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+            "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
             "dependencies": {
-                "follow-redirects": "^1.15.4",
+                "follow-redirects": "^1.15.6",
                 "form-data": "^4.0.0",
                 "proxy-from-env": "^1.1.0"
             }
@@ -1338,9 +1341,9 @@
             "dev": true
         },
         "node_modules/bare-events": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz",
-            "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==",
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.1.tgz",
+            "integrity": "sha512-9GYPpsPFvrWBkelIhOhTWtkeZxVxZOdb3VnFTCzlOo3OjvmTvzLoZFUT8kNFACx0vJej6QPney1Cf9BvzCNE/A==",
             "optional": true
         },
         "node_modules/base64-js": {
@@ -1886,9 +1889,9 @@
             }
         },
         "node_modules/dotenv-vault": {
-            "version": "1.26.0",
-            "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.26.0.tgz",
-            "integrity": "sha512-2PNnlprtOdFEG9+hAAZxXegcjlJVZMSy88arnRR4YjwU/PwkDbdtk1uzw/D88D5EZ0b84n7YVQ6RccRXmW/Qzg==",
+            "version": "1.26.1",
+            "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.26.1.tgz",
+            "integrity": "sha512-v+RK6LXpJQWhaelTT2s0b5FQB0qziRBuGCrAgAeDHtgkDEA0NqF7OXYXsrnKTuCPnwBg0FmNJr4lZebCpJnrFA==",
             "dev": true,
             "dependencies": {
                 "@oclif/core": "^1",
@@ -1896,7 +1899,7 @@
                 "@oclif/plugin-not-found": "^2.3.34",
                 "@oclif/plugin-update": "^3.1.16",
                 "@oclif/plugin-warn-if-update-available": "^2.0.46",
-                "axios": "^0.27.2",
+                "axios": "^1.6.7",
                 "chalk": "^4.1.2",
                 "dotenv": "^16.3.1"
             },
@@ -1907,16 +1910,6 @@
                 "node": ">=16"
             }
         },
-        "node_modules/dotenv-vault/node_modules/axios": {
-            "version": "0.27.2",
-            "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
-            "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
-            "dev": true,
-            "dependencies": {
-                "follow-redirects": "^1.14.9",
-                "form-data": "^4.0.0"
-            }
-        },
         "node_modules/ecdsa-sig-formatter": {
             "version": "1.0.11",
             "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -2009,17 +2002,17 @@
             }
         },
         "node_modules/eslint": {
-            "version": "8.56.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
-            "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+            "version": "8.57.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+            "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
             "dev": true,
             "peer": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.6.1",
                 "@eslint/eslintrc": "^2.1.4",
-                "@eslint/js": "8.56.0",
-                "@humanwhocodes/config-array": "^0.11.13",
+                "@eslint/js": "8.57.0",
+                "@humanwhocodes/config-array": "^0.11.14",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@nodelib/fs.walk": "^1.2.8",
                 "@ungap/structured-clone": "^1.2.0",
@@ -2446,9 +2439,9 @@
             "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
         },
         "node_modules/follow-redirects": {
-            "version": "1.15.5",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
-            "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
+            "version": "1.15.6",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+            "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
             "funding": [
                 {
                     "type": "individual",
@@ -2786,9 +2779,9 @@
             }
         },
         "node_modules/hasown": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
-            "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+            "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
             "dependencies": {
                 "function-bind": "^1.1.2"
             },
@@ -3625,9 +3618,9 @@
             }
         },
         "node_modules/node-abi": {
-            "version": "3.55.0",
-            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.55.0.tgz",
-            "integrity": "sha512-uPEjtyh2tFEvWYt4Jw7McOD5FPcHkcxm/tHZc5PWaDB3JYq0rGFUbgaAK+CT5pYpQddBfsZVWI08OwoRfdfbcQ==",
+            "version": "3.56.0",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz",
+            "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==",
             "dev": true,
             "dependencies": {
                 "semver": "^7.3.5"
@@ -4096,11 +4089,11 @@
             }
         },
         "node_modules/qs": {
-            "version": "6.11.2",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
-            "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+            "version": "6.12.0",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz",
+            "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==",
             "dependencies": {
-                "side-channel": "^1.0.4"
+                "side-channel": "^1.0.6"
             },
             "engines": {
                 "node": ">=0.6"
@@ -4357,16 +4350,16 @@
             }
         },
         "node_modules/set-function-length": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
-            "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+            "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
             "dependencies": {
-                "define-data-property": "^1.1.2",
+                "define-data-property": "^1.1.4",
                 "es-errors": "^1.3.0",
                 "function-bind": "^1.1.2",
-                "get-intrinsic": "^1.2.3",
+                "get-intrinsic": "^1.2.4",
                 "gopd": "^1.0.1",
-                "has-property-descriptors": "^1.0.1"
+                "has-property-descriptors": "^1.0.2"
             },
             "engines": {
                 "node": ">= 0.4"
@@ -4394,11 +4387,11 @@
             }
         },
         "node_modules/side-channel": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
-            "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+            "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
             "dependencies": {
-                "call-bind": "^1.0.6",
+                "call-bind": "^1.0.7",
                 "es-errors": "^1.3.0",
                 "get-intrinsic": "^1.2.4",
                 "object-inspect": "^1.13.1"
@@ -4753,9 +4746,9 @@
             }
         },
         "node_modules/ts-api-utils": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
-            "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+            "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
             "dev": true,
             "engines": {
                 "node": ">=16"
@@ -4849,9 +4842,9 @@
             }
         },
         "node_modules/typescript": {
-            "version": "5.3.3",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
-            "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+            "version": "5.4.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
+            "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
             "dev": true,
             "bin": {
                 "tsc": "bin/tsc",
@@ -4947,9 +4940,9 @@
             }
         },
         "node_modules/winston": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
-            "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==",
+            "version": "3.12.0",
+            "resolved": "https://registry.npmjs.org/winston/-/winston-3.12.0.tgz",
+            "integrity": "sha512-OwbxKaOlESDi01mC9rkM0dQqQt2I8DAUMRLZ/HpbwvDXm85IryEHgoogy5fziQy38PntgZsLlhAYHz//UPHZ5w==",
             "dependencies": {
                 "@colors/colors": "^1.6.0",
                 "@dabh/diagnostics": "^2.0.2",
@@ -4961,7 +4954,7 @@
                 "safe-stable-stringify": "^2.3.1",
                 "stack-trace": "0.0.x",
                 "triple-beam": "^1.3.0",
-                "winston-transport": "^4.5.0"
+                "winston-transport": "^4.7.0"
             },
             "engines": {
                 "node": ">= 12.0.0"
@@ -5028,9 +5021,12 @@
             "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
         },
         "node_modules/yaml": {
-            "version": "2.3.4",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
-            "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
+            "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
+            "bin": {
+                "yaml": "bin.mjs"
+            },
             "engines": {
                 "node": ">= 14"
             }
@@ -5093,9 +5089,9 @@
             }
         },
         "node_modules/zod-validation-error": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.2.tgz",
-            "integrity": "sha512-21xGaDmnU7lJZ4J63n5GXWqi+rTzGy3gDHbuZ1jP6xrK/DEQGyOqs/xW7eH96tIfCOYm+ecCuT0bfajBRKEVUw==",
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.3.tgz",
+            "integrity": "sha512-cETTrcMq3Ze58vhdR0zD37uJm/694I6mAxcf/ei5bl89cC++fBNxrC2z8lkFze/8hVMPwrbtrwXHR2LB50fpHw==",
             "engines": {
                 "node": ">=18.0.0"
             },
diff --git a/NodeApp/package.json b/NodeApp/package.json
index 87b5a5fcb2eda88e01efea2f3a8e4cdcd0b61c1b..0a191ab34aa9a8ac8f9cb73437c916dcdd31dc73 100644
--- a/NodeApp/package.json
+++ b/NodeApp/package.json
@@ -33,29 +33,31 @@
         "test"        : "echo \"Error: no test specified\" && exit 1"
     },
     "dependencies"   : {
-        "@gitbeaker/rest"     : "^39.34.3",
-        "appdata-path"        : "^1.0.0",
-        "axios"               : "^1.6.5",
-        "boxen"               : "^5.1.2",
-        "chalk"               : "^4.1.2",
-        "commander"           : "^11.1.0",
-        "dotenv"              : "^16.3.1",
-        "dotenv-expand"       : "^10.0.0",
-        "form-data"           : "^4.0.0",
-        "fs-extra"            : "^11.2.0",
-        "http-status-codes"   : "^2.3.0",
-        "inquirer"            : "^8.2.6",
-        "json5"               : "^2.2.3",
-        "jsonwebtoken"        : "^8.5.1",
-        "open"                : "^8.4.2",
-        "ora"                 : "^5.4.1",
-        "semver"              : "^7.5.4",
-        "tar-stream"          : "^3.1.6",
-        "winston"             : "^3.11.0",
-        "winston-transport"   : "^4.7.0",
-        "yaml"                : "^2.3.4",
-        "zod"                 : "^3.22.4",
-        "zod-validation-error": "^3.0.0"
+        "@gitbeaker/rest"           : "^40.0.1",
+        "@gitbeaker/core"           : "^40.0.1",
+        "@gitbeaker/requester-utils": "^40.0.1",
+        "appdata-path"              : "^1.0.0",
+        "axios"                     : "^1.6.5",
+        "boxen"                     : "^5.1.2",
+        "chalk"                     : "^4.1.2",
+        "commander"                 : "^11.1.0",
+        "dotenv"                    : "^16.3.1",
+        "dotenv-expand"             : "^10.0.0",
+        "form-data"                 : "^4.0.0",
+        "fs-extra"                  : "^11.2.0",
+        "http-status-codes"         : "^2.3.0",
+        "inquirer"                  : "^8.2.6",
+        "json5"                     : "^2.2.3",
+        "jsonwebtoken"              : "^8.5.1",
+        "open"                      : "^8.4.2",
+        "ora"                       : "^5.4.1",
+        "semver"                    : "^7.5.4",
+        "tar-stream"                : "^3.1.6",
+        "winston"                   : "^3.11.0",
+        "winston-transport"         : "^4.7.0",
+        "yaml"                      : "^2.3.4",
+        "zod"                       : "^3.22.4",
+        "zod-validation-error"      : "^3.0.0"
     },
     "devDependencies": {
         "@types/fs-extra"                 : "^11.0.4",
diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts
index fed344e1177221865e279869473aabd492d28215..1bff535cc92cf37eec53fe663314d78e3396a1fa 100644
--- a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts
+++ b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts
@@ -2,16 +2,23 @@ import CommanderCommand   from '../../CommanderCommand';
 import ora                from 'ora';
 import AccessesHelper     from '../../../helpers/AccessesHelper';
 import Assignment         from '../../../sharedByClients/models/Assignment';
-import GitlabUser         from '../../../shared/types/Gitlab/GitlabUser';
-import GitlabManager      from '../../../managers/GitlabManager';
 import DojoBackendManager from '../../../managers/DojoBackendManager';
 import Toolbox            from '../../../shared/helpers/Toolbox';
+import * as Gitlab        from '@gitbeaker/rest';
 import TextStyle          from '../../../types/TextStyle';
+import GitlabManager      from '../../../managers/GitlabManager';
+
+
+type CommandOptions = { name: string, template?: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }
 
 
 class AssignmentCreateCommand extends CommanderCommand {
     protected commandName: string = 'create';
 
+    private members!: Array<Gitlab.UserSchema> | undefined;
+    private templateIdOrNamespace: string | null = null;
+    private assignment!: Assignment;
+
     protected defineCommand() {
         this.command
             .description('create a new repository for an assignment')
@@ -23,76 +30,71 @@ class AssignmentCreateCommand extends CommanderCommand {
             .action(this.commandAction.bind(this));
     }
 
-    protected async commandAction(options: { name: string, template?: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }): Promise<void> {
-        let members!: Array<GitlabUser> | false;
-        let templateIdOrNamespace: string | null = null;
-        let assignment!: Assignment;
+    private async dataRetrieval(options: CommandOptions) {
+        console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
 
-        // Check access and retrieve data
-        {
-            console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
+        if ( !await AccessesHelper.checkTeachingStaff() ) {
+            throw new Error();
+        }
 
-            if ( !await AccessesHelper.checkTeachingStaff() ) {
-                return;
-            }
+        this.members = await GitlabManager.fetchMembers(options);
+        if ( !this.members ) {
+            throw new Error();
+        }
+
+        const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start();
+        if ( await DojoBackendManager.getAssignment(options.name) ) {
+            assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`);
+            throw new Error();
+        }
+        assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`);
+
+        if ( options.template ) {
+            this.templateIdOrNamespace = options.template;
 
-            members = await GitlabManager.fetchMembers(options);
-            if ( !members ) {
-                return;
+            if ( Number.isNaN(Number(this.templateIdOrNamespace)) ) {
+                this.templateIdOrNamespace = Toolbox.urlToPath(this.templateIdOrNamespace);
             }
 
-            const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start();
-            if ( await DojoBackendManager.getAssignment(options.name) ) {
-                assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`);
-                return;
+            if ( !await DojoBackendManager.checkTemplateAccess(this.templateIdOrNamespace) ) {
+                throw new Error();
             }
-            assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`);
+        }
+    }
 
-            if ( options.template ) {
-                templateIdOrNamespace = options.template;
+    private async createAssignment(options: CommandOptions) {
+        console.log(TextStyle.BLOCK('Please wait while we are creating the assignment (approximately 10 seconds)...'));
 
-                if ( Number.isNaN(Number(templateIdOrNamespace)) ) {
-                    templateIdOrNamespace = Toolbox.urlToPath(templateIdOrNamespace);
-                }
+        this.assignment = await DojoBackendManager.createAssignment(options.name, this.members!, this.templateIdOrNamespace);
 
-                if ( !await DojoBackendManager.checkTemplateAccess(encodeURIComponent(templateIdOrNamespace)) ) {
-                    return;
-                }
-            }
-        }
+        const oraInfo = (message: string) => {
+            ora({
+                    text  : message,
+                    indent: 4
+                }).start().info();
+        };
 
-        // Create the assignment
-        {
-            console.log(TextStyle.BLOCK('Please wait while we are creating the assignment (approximately 10 seconds)...'));
-
-            try {
-                assignment = await DojoBackendManager.createAssignment(options.name, members, templateIdOrNamespace);
-
-                const oraInfo = (message: string) => {
-                    ora({
-                            text  : message,
-                            indent: 4
-                        }).start().info();
-                };
-
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ assignment.name }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ assignment.gitlabCreationInfo.web_url }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ assignment.gitlabCreationInfo.http_url_to_repo }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ assignment.gitlabCreationInfo.ssh_url_to_repo }`);
-            } catch ( error ) {
-                return;
-            }
-        }
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ this.assignment.name }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ this.assignment.gitlabCreationInfo.web_url }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ this.assignment.gitlabCreationInfo.http_url_to_repo }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ this.assignment.gitlabCreationInfo.ssh_url_to_repo }`);
+    }
 
-        // Clone the repository
-        {
-            if ( options.clone ) {
-                console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
+    private async cloneRepository(options: CommandOptions) {
+        if ( options.clone ) {
+            console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
 
-                await GitlabManager.cloneRepository(options.clone, assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0);
-            }
+            await GitlabManager.cloneRepository(options.clone, this.assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0);
         }
     }
+
+    protected async commandAction(options: CommandOptions): Promise<void> {
+        try {
+            await this.dataRetrieval(options);
+            await this.createAssignment(options);
+            await this.cloneRepository(options);
+        } catch ( e ) { /* Do nothing */ }
+    }
 }
 
 
diff --git a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts
index d1f57e64ec809f5684fef9e09ae7c0869944cca5..e2d18f8e0cd02004939d24aab7841a5c9b8ed050 100644
--- a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts
+++ b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts
@@ -8,8 +8,8 @@ class AuthTestCommand extends CommanderCommand {
 
     protected defineCommand() {
         this.command
-        .description('test availability of registered Dojo API and Gitlab API sessions')
-        .action(this.commandAction.bind(this));
+            .description('test availability of registered Dojo API and Gitlab API sessions')
+            .action(this.commandAction.bind(this));
     }
 
     protected async commandAction(): Promise<void> {
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
index 2fc6bf7bb8da17746bf56cfceed927aed91fb65e..8a0d7ea5aa83a020396d9aa09019c3c6f30552c1 100644
--- a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts
@@ -1,17 +1,24 @@
 import CommanderCommand   from '../../CommanderCommand';
-import GitlabManager      from '../../../managers/GitlabManager';
-import GitlabUser         from '../../../shared/types/Gitlab/GitlabUser';
 import ora                from 'ora';
 import DojoBackendManager from '../../../managers/DojoBackendManager';
 import AccessesHelper     from '../../../helpers/AccessesHelper';
 import Assignment         from '../../../sharedByClients/models/Assignment';
 import Exercise           from '../../../sharedByClients/models/Exercise';
+import * as Gitlab        from '@gitbeaker/rest';
 import TextStyle          from '../../../types/TextStyle';
+import GitlabManager      from '../../../managers/GitlabManager';
+
+
+type CommandOptions = { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }
 
 
 class ExerciseCreateCommand extends CommanderCommand {
     protected commandName: string = 'create';
 
+    private members!: Array<Gitlab.UserSchema> | undefined;
+    private assignment!: Assignment | undefined;
+    private exercise!: Exercise;
+
     protected defineCommand() {
         this.command
             .description('create a new exercise from an assignment')
@@ -22,80 +29,76 @@ class ExerciseCreateCommand extends CommanderCommand {
             .action(this.commandAction.bind(this));
     }
 
-    protected async commandAction(options: { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }): Promise<void> {
-        let members!: Array<GitlabUser> | false;
-        let assignment!: Assignment | undefined;
-        let exercise!: Exercise;
-
-        // Check access and retrieve data
-        {
-            console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
-
-            if ( !await AccessesHelper.checkStudent() ) {
-                return;
-            }
-
-            members = await GitlabManager.fetchMembers(options);
-            if ( !members ) {
-                return;
-            }
-
-            ora('Checking assignment:').start().info();
-            const assignmentGetSpinner: ora.Ora = ora({
-                                                          text  : 'Checking if assignment exists',
-                                                          indent: 4
-                                                      }).start();
-            assignment = await DojoBackendManager.getAssignment(options.assignment);
-            if ( !assignment ) {
-                assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
-                return;
-            }
-            assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
-
-            const assignmentPublishedSpinner: ora.Ora = ora({
-                                                                text  : 'Checking if assignment is published',
-                                                                indent: 4
-                                                            }).start();
-            if ( !assignment.published ) {
-                assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`);
-                return;
-            }
-            assignmentPublishedSpinner.succeed(`Assignment "${ assignment.name }" is published`);
+    private async dataRetrieval(options: CommandOptions) {
+        console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...'));
+
+        if ( !await AccessesHelper.checkStudent() ) {
+            throw new Error();
+        }
+
+        this.members = await GitlabManager.fetchMembers(options);
+        if ( !this.members ) {
+            throw new Error();
         }
 
-        //Create the exercise
-        {
-            console.log(TextStyle.BLOCK('Please wait while we are creating the exercise (approximately 10 seconds)...'));
-
-            try {
-                exercise = await DojoBackendManager.createExercise(assignment.name, members);
-
-                const oraInfo = (message: string) => {
-                    ora({
-                            text  : message,
-                            indent: 4
-                        }).start().info();
-                };
-
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ exercise.id }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ exercise.name }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
-                oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
-            } catch ( error ) {
-                return;
-            }
+        ora('Checking assignment:').start().info();
+        const assignmentGetSpinner: ora.Ora = ora({
+                                                      text  : 'Checking if assignment exists',
+                                                      indent: 4
+                                                  }).start();
+        this.assignment = await DojoBackendManager.getAssignment(options.assignment);
+        if ( !this.assignment ) {
+            assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
+            throw new Error();
+        }
+        assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
+
+        const assignmentPublishedSpinner: ora.Ora = ora({
+                                                            text  : 'Checking if assignment is published',
+                                                            indent: 4
+                                                        }).start();
+        if ( !this.assignment.published ) {
+            assignmentPublishedSpinner.fail(`Assignment "${ this.assignment.name }" isn't published`);
+            throw new Error();
         }
+        assignmentPublishedSpinner.succeed(`Assignment "${ this.assignment.name }" is published`);
+    }
+
+    private async createExercise() {
+        console.log(TextStyle.BLOCK('Please wait while we are creating the exercise (approximately 10 seconds)...'));
+
+        this.exercise = await DojoBackendManager.createExercise(this.assignment!.name, this.members!);
 
-        // Clone the repository
-        {
-            if ( options.clone ) {
-                console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
+        const oraInfo = (message: string) => {
+            ora({
+                    text  : message,
+                    indent: 4
+                }).start().info();
+        };
+
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ this.exercise.id }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ this.exercise.name }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ this.exercise.gitlabCreationInfo.web_url }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ this.exercise.gitlabCreationInfo.http_url_to_repo }`);
+        oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ this.exercise.gitlabCreationInfo.ssh_url_to_repo }`);
+    }
 
-                await GitlabManager.cloneRepository(options.clone, exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise - ${ exercise.assignmentName }`, true, 0);
-            }
+    private async cloneRepository(options: CommandOptions) {
+        if ( options.clone ) {
+            console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...'));
+
+            await GitlabManager.cloneRepository(options.clone, this.exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise - ${ this.exercise.assignmentName }`, true, 0);
         }
     }
+
+
+    protected async commandAction(options: CommandOptions): Promise<void> {
+        try {
+            await this.dataRetrieval(options);
+            await this.createExercise();
+            await this.cloneRepository(options);
+        } catch ( e ) { /* Do nothing */ }
+    }
 }
 
 
diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts
index 04464f6205fd62b86cfe19631fc1d3e988ea694d..13760c3927e123a13fc5ddb4c15048cee82944a7 100644
--- a/NodeApp/src/helpers/AutoCompletionHelper.ts
+++ b/NodeApp/src/helpers/AutoCompletionHelper.ts
@@ -67,7 +67,6 @@ complete -f -c dojo
 
 function isHidden(cmd: Command): boolean {
     return (cmd as Command & { _hidden: boolean })._hidden;
-
 }
 
 function isLeaf(cmd: Command): boolean {
diff --git a/NodeApp/src/helpers/GlobalHelper.ts b/NodeApp/src/helpers/GlobalHelper.ts
index c7dccb10389fda604b31052c01e9fe1e8e54b0cf..8ecac86046acc962b43ca8212468150e3ac0b803 100644
--- a/NodeApp/src/helpers/GlobalHelper.ts
+++ b/NodeApp/src/helpers/GlobalHelper.ts
@@ -1,5 +1,6 @@
 import { Command, Option } from 'commander';
 import Config              from '../config/Config';
+import SessionManager      from '../managers/SessionManager';
 
 
 class GlobalHelper {
@@ -12,6 +13,13 @@ class GlobalHelper {
 
         return command;
     }
+
+
+    public readonly refreshGitlabTokenFunction = async () => {
+        await SessionManager.refreshTokens();
+
+        return SessionManager.gitlabCredentials.accessToken ?? '';
+    };
 }
 
 
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index 9bf65df250b19eae0aa58a8b97885cb651be88c0..5e482210ba707662f857dd8067b974cf7947598a 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -1,7 +1,6 @@
 import axios, { AxiosError } from 'axios';
 import ora                   from 'ora';
 import ApiRoute              from '../sharedByClients/types/Dojo/ApiRoute';
-import GitlabUser            from '../shared/types/Gitlab/GitlabUser';
 import ClientsSharedConfig   from '../sharedByClients/config/ClientsSharedConfig';
 import Assignment            from '../sharedByClients/models/Assignment';
 import DojoBackendResponse   from '../shared/types/Dojo/DojoBackendResponse';
@@ -9,7 +8,9 @@ import Exercise              from '../sharedByClients/models/Exercise';
 import GitlabToken           from '../shared/types/Gitlab/GitlabToken';
 import User                  from '../sharedByClients/models/User';
 import DojoStatusCode        from '../shared/types/Dojo/DojoStatusCode';
+import * as Gitlab           from '@gitbeaker/rest';
 import DojoBackendHelper     from '../sharedByClients/helpers/Dojo/DojoBackendHelper';
+import GitlabPipelineStatus  from '../shared/types/Gitlab/GitlabPipelineStatus';
 
 
 class DojoBackendManager {
@@ -20,20 +21,35 @@ class DojoBackendManager {
                     case DojoStatusCode.ASSIGNMENT_EXERCISE_NOT_RELATED:
                         spinner.fail(`The exercise does not belong to the assignment.`);
                         break;
+                    case DojoStatusCode.ASSIGNMENT_PUBLISH_NO_PIPELINE:
+                        spinner.fail(`No pipeline found for this assignment.`);
+                        break;
+                    case DojoStatusCode.ASSIGNMENT_PUBLISH_PIPELINE_FAILED:
+                        spinner.fail(error.response?.data?.message ?? `Last pipeline status is not "${ GitlabPipelineStatus.SUCCESS }".`);
+                        break;
                     case DojoStatusCode.EXERCISE_CORRECTION_ALREADY_EXIST:
                         spinner.fail(`This exercise is already labelled as a correction. If you want to update it, please use the update command.`);
                         break;
                     case DojoStatusCode.EXERCISE_CORRECTION_NOT_EXIST:
                         spinner.fail(`The exercise is not labelled as a correction so it's not possible to update it.`);
                         break;
+                    case DojoStatusCode.ASSIGNMENT_NAME_CONFLICT:
+                        spinner.fail(`Assignment creation error: The assignment name already exists. Please choose another name.`);
+                        break;
                     case DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR:
-                        spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment on Gitlab. Please try again later or contact an administrator.`);
+                        spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment on GitLab (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`);
+                        break;
+                    case DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR:
+                        spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`);
                         break;
                     case DojoStatusCode.MAX_EXERCISE_PER_ASSIGNMENT_REACHED:
-                        spinner.fail(`The following users have reached the maximum number of exercise of this assignment : ${ ((error.response.data as DojoBackendResponse<Array<GitlabUser>>).data).map(user => user.name).join(', ') }.`);
+                        spinner.fail(`The following users have reached the maximum number of exercise of this assignment : ${ ((error.response.data as DojoBackendResponse<Array<Gitlab.UserSchema>>).data).map(user => user.name).join(', ') }.`);
+                        break;
+                    case DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR:
+                        spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`);
                         break;
                     case DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR:
-                        spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise on Gitlab. Please try again later or contact an administrator.`);
+                        spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise on GitLab (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`);
                         break;
                     case DojoStatusCode.GITLAB_TEMPLATE_NOT_FOUND:
                         spinner.fail(`Template not found or access denied. Please check the template ID or url. Also, please check that the template have public/internal visibility or that your and Dojo account (${ ClientsSharedConfig.gitlab.dojoAccount.username }) have at least reporter role to the template (if private).`);
@@ -106,7 +122,7 @@ class DojoBackendManager {
         }
     }
 
-    public async createAssignment(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> {
+    public async createAssignment(name: string, members: Array<Gitlab.UserSchema>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> {
         const spinner: ora.Ora = ora('Creating assignment...');
 
         if ( verbose ) {
@@ -131,7 +147,7 @@ class DojoBackendManager {
         }
     }
 
-    public async createExercise(assignmentName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercise> {
+    public async createExercise(assignmentName: string, members: Array<Gitlab.UserSchema>, verbose: boolean = true): Promise<Exercise> {
         const spinner: ora.Ora = ora('Creating exercise...');
 
         if ( verbose ) {
@@ -169,13 +185,7 @@ class DojoBackendManager {
 
             return;
         } catch ( error ) {
-            if ( verbose ) {
-                if ( error instanceof AxiosError && error.response ) {
-                    spinner.fail(`Assignment visibility change error: ${ error.response.statusText }`);
-                } else {
-                    spinner.fail(`Assignment visibility change error: unknown error`);
-                }
-            }
+            this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`);
 
             throw error;
         }
diff --git a/NodeApp/src/managers/GitlabManager.ts b/NodeApp/src/managers/GitlabManager.ts
index f8ee3f16dd36dfc68418dd6eab1baf2384fc64ad..9291be49dced0d043b285bd14824eecd5bb0f702 100644
--- a/NodeApp/src/managers/GitlabManager.ts
+++ b/NodeApp/src/managers/GitlabManager.ts
@@ -1,16 +1,18 @@
-import axios            from 'axios';
-import ora              from 'ora';
-import GitlabUser       from '../shared/types/Gitlab/GitlabUser';
-import GitlabRoute      from '../shared/types/Gitlab/GitlabRoute';
-import SharedConfig     from '../shared/config/SharedConfig';
-import GitlabRepository from '../shared/types/Gitlab/GitlabRepository';
-import fs               from 'fs-extra';
-import { spawn }        from 'child_process';
-
-
-class GitlabManager {
-    private getApiUrl(route: GitlabRoute): string {
-        return `${ SharedConfig.gitlab.apiURL }${ route }`;
+import ora                                       from 'ora';
+import fs                                        from 'fs-extra';
+import { spawn }                                 from 'child_process';
+import { NotificationSettingSchema, UserSchema } from '@gitbeaker/rest';
+import * as GitlabCore                           from '@gitbeaker/core';
+import SharedGitlabManager                       from '../shared/managers/SharedGitlabManager';
+import GlobalHelper                              from '../helpers/GlobalHelper';
+
+
+type getGitlabUser = (param: number | string) => Promise<UserSchema | undefined>
+
+
+class GitlabManager extends SharedGitlabManager {
+    constructor() {
+        super('', GlobalHelper.refreshGitlabTokenFunction.bind(GlobalHelper));
     }
 
     public async testToken(verbose: boolean = true): Promise<[ boolean, boolean ]> {
@@ -35,7 +37,7 @@ class GitlabManager {
             }
 
             try {
-                notificationSettings = (await this.getNotificationSettings()).data as NotificationSettings;
+                notificationSettings = await this.getNotificationSettings();
 
                 result[0] = true;
 
@@ -83,15 +85,15 @@ class GitlabManager {
         return result;
     }
 
-    public getNotificationSettings() {
-        return axios.get(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS));
+    public getNotificationSettings(): Promise<NotificationSettingSchema> {
+        return this.executeGitlabRequest(() => this.api.NotificationSettings.show());
     }
 
-    public setNotificationSettings(newSettings: Record<string, string>) {
-        return axios.put(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS), { params: new URLSearchParams(newSettings) });
+    public setNotificationSettings(newSettings: GitlabCore.EditNotificationSettingsOptions) {
+        return this.executeGitlabRequest(() => this.api.NotificationSettings.edit(newSettings));
     }
 
-    private async getGitlabUsers(paramsToSearch: Array<string | number>, paramName: string, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
+    private async getGitlabUsers(paramsToSearch: Array<string | number>, searchFunction: getGitlabUser, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> {
         try {
             return await Promise.all(paramsToSearch.map(async param => {
                 const spinner: ora.Ora = ora({
@@ -101,18 +103,14 @@ class GitlabManager {
                 if ( verbose ) {
                     spinner.start();
                 }
-                const params: { [key: string]: unknown } = {};
-                params[paramName] = param;
-                const user = await axios.get<Array<GitlabUser>>(this.getApiUrl(GitlabRoute.USERS_GET), { params: params });
 
-                if ( user.data[0] ) {
-                    const gitlabUser = user.data[0];
+                const user = await searchFunction(param);
 
+                if ( user ) {
                     if ( verbose ) {
-                        spinner.succeed(`${ gitlabUser.username } (${ gitlabUser.id })`);
+                        spinner.succeed(`${ user.username } (${ user.id })`);
                     }
-
-                    return gitlabUser;
+                    return user;
                 } else {
                     if ( verbose ) {
                         spinner.fail(`${ param }`);
@@ -126,30 +124,26 @@ class GitlabManager {
         }
     }
 
-    public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
-        return this.getGitlabUsers(ids, 'id', verbose, verboseIndent);
+    public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> {
+        return this.getGitlabUsers(ids, this.getUserById.bind(this) as getGitlabUser, verbose, verboseIndent);
     }
 
-    public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
-        return this.getGitlabUsers(usernames, 'search', verbose, verboseIndent);
+    public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> {
+        return this.getGitlabUsers(usernames, this.getUserByUsername.bind(this) as getGitlabUser, verbose, verboseIndent);
     }
 
-    public async getRepository(repoId: number): Promise<GitlabRepository> {
-        return axios.get(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', repoId.toString()));
-    }
-
-    public async fetchMembers(options: { members_id?: Array<number>, members_username?: Array<string> }): Promise<Array<GitlabUser> | false> {
+    public async fetchMembers(options: { members_id?: Array<number>, members_username?: Array<string> }): Promise<Array<UserSchema> | undefined> {
         if ( options.members_id || options.members_username ) {
             ora('Checking Gitlab members:').start().info();
         }
 
-        let members: Array<GitlabUser> = [];
+        let members: Array<UserSchema> = [];
 
-        async function getMembers<T>(context: unknown, functionName: string, paramsToSearch: Array<T>): Promise<boolean> {
-            const gitlabUsers = await ((context as { [functionName: string]: (arg: Array<T>, verbose: boolean, verboseIndent: number) => Promise<Array<GitlabUser | undefined>> })[functionName])(paramsToSearch, true, 8);
+        async function isUsersExists<T>(context: unknown, functionName: string, paramsToSearch: Array<T>): Promise<boolean> {
+            const users = await ((context as { [functionName: string]: (arg: Array<T>, verbose: boolean, verboseIndent: number) => Promise<Array<UserSchema | undefined>> })[functionName])(paramsToSearch, true, 8);
 
-            if ( gitlabUsers.every(user => user) ) {
-                members = members.concat(gitlabUsers as Array<GitlabUser>);
+            if ( users.every(user => user) ) {
+                members = members.concat(users as Array<UserSchema>);
                 return true;
             } else {
                 return false;
@@ -164,7 +158,7 @@ class GitlabManager {
                     indent: 4
                 }).start().info();
 
-            result = await getMembers(this, 'getUsersById', options.members_id);
+            result = await isUsersExists(this, 'getUsersById', options.members_id);
         }
 
         if ( options.members_username ) {
@@ -173,16 +167,10 @@ class GitlabManager {
                     indent: 4
                 }).start().info();
 
-            result = result && await getMembers(this, 'getUsersByUsername', options.members_username);
+            result = result && await isUsersExists(this, 'getUsersByUsername', options.members_username);
         }
 
-        if ( !result ) {
-            return false;
-        }
-
-        members = members.removeObjectDuplicates(gitlabUser => gitlabUser.id);
-
-        return members;
+        return result ? members.removeObjectDuplicates(gitlabUser => gitlabUser.id) : undefined;
     }
 
     public async cloneRepository(clonePath: string | boolean, repositorySshUrl: string, folderName?: string, verbose: boolean = false, verboseIndent: number = 0) {
diff --git a/NodeApp/src/managers/HttpManager.ts b/NodeApp/src/managers/HttpManager.ts
index a28fc2162edd5ad3aa5082a33328d4eeb9bcf8d7..be8ac3b5326c335db571fb8562c44aadb13478aa 100644
--- a/NodeApp/src/managers/HttpManager.ts
+++ b/NodeApp/src/managers/HttpManager.ts
@@ -1,15 +1,14 @@
-import axios, { AxiosError, AxiosRequestConfig, AxiosRequestHeaders } from 'axios';
-import SessionManager                                                 from './SessionManager';
-import FormData                                                       from 'form-data';
-import { StatusCodes }                                                from 'http-status-codes';
-import ClientsSharedConfig                                            from '../sharedByClients/config/ClientsSharedConfig';
-import { version }                                                    from '../config/Version';
-import DojoBackendResponse                                            from '../shared/types/Dojo/DojoBackendResponse';
-import DojoStatusCode                                                 from '../shared/types/Dojo/DojoStatusCode';
-import boxen                                                          from 'boxen';
-import SharedConfig                                                   from '../shared/config/SharedConfig';
-import { stateConfigFile }                                            from '../config/ConfigFiles';
-import TextStyle                                                      from '../types/TextStyle';
+import axios, { AxiosError, AxiosRequestHeaders } from 'axios';
+import SessionManager                             from './SessionManager';
+import FormData                                   from 'form-data';
+import { StatusCodes }                            from 'http-status-codes';
+import ClientsSharedConfig                        from '../sharedByClients/config/ClientsSharedConfig';
+import { version }                                from '../config/Version';
+import DojoBackendResponse                        from '../shared/types/Dojo/DojoBackendResponse';
+import DojoStatusCode                             from '../shared/types/Dojo/DojoStatusCode';
+import boxen                                      from 'boxen';
+import { stateConfigFile }                        from '../config/ConfigFiles';
+import TextStyle                                  from '../types/TextStyle';
 
 
 class HttpManager {
@@ -22,7 +21,6 @@ class HttpManager {
 
     private registerRequestInterceptor() {
         axios.interceptors.request.use(config => {
-
             if ( config.data instanceof FormData ) {
                 config.headers = { ...config.headers, ...config.data.getHeaders() } as AxiosRequestHeaders;
             }
@@ -42,10 +40,6 @@ class HttpManager {
                 config.headers['client-version'] = version;
             }
 
-            if ( SessionManager.gitlabCredentials.accessToken && config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) {
-                config.headers.Authorization = `Bearer ${ SessionManager.gitlabCredentials.accessToken }`;
-            }
-
             return config;
         });
     }
@@ -82,45 +76,26 @@ class HttpManager {
 
     private apiAuthorizationError(error: AxiosError, isFromApi: boolean) {
         if ( this.handleAuthorizationCommandErrors && isFromApi && error.response ) {
-            switch ( error.response.status ) {
-                case StatusCodes.UNAUTHORIZED:
-                    this.requestError('Session expired or does not exist. Please login again.');
-                    break;
-                case StatusCodes.FORBIDDEN:
-                    this.requestError('Forbidden access.');
-                    break;
-                default:
-                    this.requestError('Unknown error.');
-                    break;
+            const errorCustomCode = (error.response.data as DojoBackendResponse<unknown> | undefined)?.code ?? error.response.status;
+            
+            if ( errorCustomCode === error.response.status ) {
+                switch ( error.response.status ) {
+                    case StatusCodes.UNAUTHORIZED:
+                        this.requestError('Session expired or does not exist. Please login again.');
+                        break;
+                    case StatusCodes.FORBIDDEN:
+                        this.requestError('Forbidden access.');
+                        break;
+                    default:
+                        this.requestError('Unknown error.');
+                        break;
+                }
             }
         } else {
             this.handleAuthorizationCommandErrors = true;
         }
     }
 
-    private async gitlabAuthorizationError(error: AxiosError, isFromGitlab: boolean): Promise<Promise<unknown> | undefined> {
-        const originalConfig = Object.assign({}, error.config, { _retry: false });
-
-        // Try to refresh the Gitlab tokens if the request have failed with a 401 error
-        if ( error.response && error.response.status === StatusCodes.UNAUTHORIZED && isFromGitlab && !originalConfig?._retry ) {
-            originalConfig._retry = true;
-
-            try {
-                await SessionManager.refreshTokens();
-
-                return axios(error.config as AxiosRequestConfig);
-            } catch ( subError ) {
-                if ( subError instanceof AxiosError && subError.response && subError.response.data ) {
-                    return Promise.reject(subError.response.data);
-                }
-
-                return Promise.reject(subError);
-            }
-        }
-
-        return undefined;
-    }
-
     private registerResponseInterceptor() {
         axios.interceptors.response.use(response => {
             if ( response.data && response.data.sessionToken ) {
@@ -140,11 +115,8 @@ class HttpManager {
             return response;
         }, async error => {
             if ( error.response ) {
-
                 const isFromApi = error.response.config.url && error.response.config.url.indexOf(ClientsSharedConfig.apiURL) !== -1;
-                const isFromGitlab = error.response.config.url && error.response.config.url.indexOf(SharedConfig.gitlab.URL) !== -1;
 
-                await this.gitlabAuthorizationError(error, isFromGitlab);
                 this.apiMethodNotAllowed(error, isFromApi);
                 this.apiAuthorizationError(error, isFromApi);
             } else {
diff --git a/NodeApp/src/managers/SessionManager.ts b/NodeApp/src/managers/SessionManager.ts
index c25748cf74760c5e7ad069e74e9b8d3e92f5e21f..1f2ed42af8b02a3705caf756210a44fca0685eac 100644
--- a/NodeApp/src/managers/SessionManager.ts
+++ b/NodeApp/src/managers/SessionManager.ts
@@ -1,27 +1,27 @@
-import * as jwt                  from 'jsonwebtoken';
-import User                      from '../sharedByClients/models/User';
-import LocalConfigKeys           from '../types/LocalConfigKeys';
-import axios, { HttpStatusCode } from 'axios';
-import HttpManager               from './HttpManager';
-import ora                       from 'ora';
-import Permissions               from '../types/Permissions';
-import ApiRoute                  from '../sharedByClients/types/Dojo/ApiRoute';
-import DojoBackendManager        from './DojoBackendManager';
-import Config                    from '../config/Config';
-import ClientsSharedConfig       from '../sharedByClients/config/ClientsSharedConfig';
-import DojoGitlabCredentials     from '../sharedByClients/types/Dojo/DojoGitlabCredentials';
-import * as http                 from 'http';
-import EventEmitter              from 'events';
-import SharedConfig              from '../shared/config/SharedConfig';
-import chalk                     from 'chalk';
-import inquirer                  from 'inquirer';
-import SharedGitlabManager       from '../shared/managers/SharedGitlabManager';
-import GitlabManager             from './GitlabManager';
-import GitlabToken               from '../shared/types/Gitlab/GitlabToken';
-import open                      from 'open';
-import { sessionConfigFile }     from '../config/ConfigFiles';
-import TextStyle                 from '../types/TextStyle';
-import DojoBackendHelper         from '../sharedByClients/helpers/Dojo/DojoBackendHelper';
+import * as jwt              from 'jsonwebtoken';
+import User                  from '../sharedByClients/models/User';
+import LocalConfigKeys       from '../types/LocalConfigKeys';
+import axios                 from 'axios';
+import HttpManager           from './HttpManager';
+import ora                   from 'ora';
+import Permissions           from '../types/Permissions';
+import ApiRoute              from '../sharedByClients/types/Dojo/ApiRoute';
+import DojoBackendManager    from './DojoBackendManager';
+import Config                from '../config/Config';
+import ClientsSharedConfig   from '../sharedByClients/config/ClientsSharedConfig';
+import DojoGitlabCredentials from '../sharedByClients/types/Dojo/DojoGitlabCredentials';
+import * as http             from 'http';
+import EventEmitter          from 'events';
+import SharedConfig          from '../shared/config/SharedConfig';
+import chalk                 from 'chalk';
+import inquirer              from 'inquirer';
+import GitlabToken           from '../shared/types/Gitlab/GitlabToken';
+import open                  from 'open';
+import { sessionConfigFile } from '../config/ConfigFiles';
+import TextStyle             from '../types/TextStyle';
+import DojoBackendHelper     from '../sharedByClients/helpers/Dojo/DojoBackendHelper';
+import GitlabManager         from './GitlabManager';
+import { StatusCodes }       from 'http-status-codes';
 
 
 class LoginServer {
@@ -32,7 +32,7 @@ class LoginServer {
         this.server = http.createServer((req, res) => {
             const sendError = (error: string) => {
                 this.events.emit('error', error);
-                res.writeHead(HttpStatusCode.InternalServerError, { 'Content-Type': 'text/html' });
+                res.writeHead(StatusCodes.INTERNAL_SERVER_ERROR, { 'Content-Type': 'text/html' });
                 res.write(`<html lang="en"><body><h1 style="color: red">DojoCLI login error</h1><h3>Please look at your CLI for more informations.</h3></body></html>`);
                 res.end();
             };
@@ -40,7 +40,7 @@ class LoginServer {
             if ( req.url?.match(Config.login.server.route) ) {
                 const urlParts = req.url.split('=');
                 if ( urlParts.length > 0 ) {
-                    res.writeHead(HttpStatusCode.Ok, { 'Content-Type': 'text/html' });
+                    res.writeHead(StatusCodes.OK, { 'Content-Type': 'text/html' });
                     res.write(`<html lang="en"><body><h1 style="color: green">DojoCLI login successful</h1><h3>You can close this window.</h3></body></html>`);
                     res.end();
 
@@ -116,6 +116,10 @@ class SessionManager {
 
     set gitlabCredentials(credentials: DojoGitlabCredentials) {
         sessionConfigFile.setParam(LocalConfigKeys.GITLAB, credentials);
+
+        if ( credentials.accessToken ) {
+            GitlabManager.setToken(credentials.accessToken);
+        }
     }
 
     private async getGitlabCodeFromHeadlessEnvironment(): Promise<string> {
@@ -205,7 +209,7 @@ class SessionManager {
                                         }).start();
         let gitlabTokens: GitlabToken;
         try {
-            gitlabTokens = await SharedGitlabManager.getTokens(gitlabCode);
+            gitlabTokens = await GitlabManager.getTokens(gitlabCode);
             this.gitlabCredentials = {
                 refreshToken: gitlabTokens.refresh_token,
                 accessToken : gitlabTokens.access_token
@@ -307,6 +311,13 @@ class SessionManager {
             return false;
         }
 
+        if ( checkPermissions && checkPermissions.length === 0 ) {
+            ora({
+                    text  : `Here is your permissions:`,
+                    indent: 4
+                }).start().info();
+        }
+
         return this.checkPermissions(verbose, 8, checkPermissions);
     }
 }
diff --git a/NodeApp/src/shared b/NodeApp/src/shared
index 75fedb26c47bb6f707725307a79a45a13e62496d..6e78095b3fe73f2c2987de1a3d3b55511335a2bf 160000
--- a/NodeApp/src/shared
+++ b/NodeApp/src/shared
@@ -1 +1 @@
-Subproject commit 75fedb26c47bb6f707725307a79a45a13e62496d
+Subproject commit 6e78095b3fe73f2c2987de1a3d3b55511335a2bf
diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients
index c4efbcfb2a50e7108e101fb673e84f87fad8e246..8ae82abcc7f11e595060b1a48f340290e7b2ebac 160000
--- a/NodeApp/src/sharedByClients
+++ b/NodeApp/src/sharedByClients
@@ -1 +1 @@
-Subproject commit c4efbcfb2a50e7108e101fb673e84f87fad8e246
+Subproject commit 8ae82abcc7f11e595060b1a48f340290e7b2ebac