From 5910d45b9383f87e5c583d06a3e33367a324b7fb Mon Sep 17 00:00:00 2001 From: "kelly.nguyen" <kelly.nguyen@etu.hesge.ch> Date: Thu, 27 Jun 2024 00:17:14 +0200 Subject: [PATCH] add logic to zip all assignments in one zip --- ExpressAPI/package-lock.json | 495 +++++++++++++++++++++- ExpressAPI/package.json | 139 +++--- ExpressAPI/src/managers/GitlabManager.ts | 117 +++-- ExpressAPI/src/routes/ApiRoutesManager.ts | 2 + ExpressAPI/src/routes/AssignmentRoutes.ts | 76 +++- ExpressAPI/src/routes/UserRoutes.ts | 57 +++ ExpressAPI/src/shared | 2 +- ExpressAPI/src/types/SecurityCheckType.ts | 1 + 8 files changed, 745 insertions(+), 144 deletions(-) create mode 100644 ExpressAPI/src/routes/UserRoutes.ts diff --git a/ExpressAPI/package-lock.json b/ExpressAPI/package-lock.json index 0027a6b..0987c18 100644 --- a/ExpressAPI/package-lock.json +++ b/ExpressAPI/package-lock.json @@ -12,6 +12,7 @@ "@dotenvx/dotenvx": "^0.44.1", "@gitbeaker/rest": "^40.0.3", "@prisma/client": "^5.14.0", + "archiver": "^7.0.1", "axios": "^1.7.2", "compression": "^1.7.4", "cors": "^2.8.5", @@ -1659,7 +1660,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -1804,6 +1804,180 @@ } ] }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/archiver/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2029,6 +2203,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2301,6 +2484,91 @@ "node": ">=16" } }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/compress-commons/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/compress-commons/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -2451,6 +2719,100 @@ "node": ">= 0.10" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/crc32-stream/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/crc32-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3079,7 +3441,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "engines": { "node": ">=6" } @@ -3090,6 +3451,15 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3667,6 +4037,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -4205,6 +4581,18 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4455,7 +4843,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4843,7 +5230,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8019,6 +8405,15 @@ "node": ">=6" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -8207,6 +8602,15 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -9497,6 +9901,89 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/zip-stream/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/zip-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", diff --git a/ExpressAPI/package.json b/ExpressAPI/package.json index 89dc5f3..78d1a12 100644 --- a/ExpressAPI/package.json +++ b/ExpressAPI/package.json @@ -1,80 +1,81 @@ { - "name" : "dojo_backend_api", - "description" : "Backend API of the Dojo project", - "version" : "4.1.0", - "license" : "AGPLv3", - "author" : "Michaël Minelli <dojo@minelli.me>", - "main" : "dist/src/app.js", - "scripts" : { - "clean" : "rm -R dist/*", - "dotenv:build" : "npx dotenvx encrypt", - "lint" : "npx eslint .", - "genversion" : "npx genversion -s -e src/config/Version.ts", - "build:openapi" : "sed -i -r \"1,20 s/^\\([ ]*version:\\).*$/\\1 $(jq -r .version package.json)/\" assets/OpenAPI/OpenAPI.yaml; npx @redocly/cli build-docs assets/OpenAPI/OpenAPI.yaml --output=assets/OpenAPI/redoc.html", - "build:project" : "npm run genversion; npx prisma generate && npx tsc --project ./ && cp -R assets dist/assets", - "build" : "npm run build:openapi; npm run build:project", + "name": "dojo_backend_api", + "description": "Backend API of the Dojo project", + "version": "4.1.0", + "license": "AGPLv3", + "author": "Michaël Minelli <dojo@minelli.me>", + "main": "dist/src/app.js", + "scripts": { + "clean": "rm -R dist/*", + "dotenv:build": "npx dotenvx encrypt", + "lint": "npx eslint .", + "genversion": "npx genversion -s -e src/config/Version.ts", + "build:openapi": "sed -i -r \"1,20 s/^\\([ ]*version:\\).*$/\\1 $(jq -r .version package.json)/\" assets/OpenAPI/OpenAPI.yaml; npx @redocly/cli build-docs assets/OpenAPI/OpenAPI.yaml --output=assets/OpenAPI/redoc.html", + "build:project": "npm run genversion; npx prisma generate && npx tsc --project ./ && cp -R assets dist/assets", + "build": "npm run build:openapi; npm run build:project", "database:migrate:create": "npx dotenv -e .env.development -- npx prisma migrate dev", - "database:migrate:dev" : "npx dotenv -e .env.development -- npx prisma migrate deploy", - "database:migrate:prod" : "npx prisma migrate deploy", - "database:seed:dev" : "npm run genversion; npm run build; npx dotenv -e .env.development -- npx prisma db seed", - "database:seed:prod" : "npm run genversion; npm run build; NODE_ENV=production npx prisma db seed", - "database:deploy:dev" : "npm run database:migrate:dev && npm run database:seed:dev", - "database:deploy:prod" : "npm run database:migrate:prod && npm run database:seed:prod", - "start:dev" : "npm run genversion; npx nodemon src/app.ts", - "start:prod" : "npm run genversion; NODE_ENV=production npx node --max-http-header-size=1048576 dist/src/app.js", - "start:migrate:prod" : "npm run genversion; npm run database:deploy:prod && npm run start:prod" + "database:migrate:dev": "npx dotenv -e .env.development -- npx prisma migrate deploy", + "database:migrate:prod": "npx prisma migrate deploy", + "database:seed:dev": "npm run genversion; npm run build; npx dotenv -e .env.development -- npx prisma db seed", + "database:seed:prod": "npm run genversion; npm run build; NODE_ENV=production npx prisma db seed", + "database:deploy:dev": "npm run database:migrate:dev && npm run database:seed:dev", + "database:deploy:prod": "npm run database:migrate:prod && npm run database:seed:prod", + "start:dev": "npm run genversion; npx nodemon src/app.ts", + "start:prod": "npm run genversion; NODE_ENV=production npx node --max-http-header-size=1048576 dist/src/app.js", + "start:migrate:prod": "npm run genversion; npm run database:deploy:prod && npm run start:prod" }, - "prisma" : { + "prisma": { "seed": "node dist/prisma/seed" }, - "dependencies" : { - "@dotenvx/dotenvx" : "^0.44.1", - "@gitbeaker/rest" : "^40.0.3", - "@prisma/client" : "^5.14.0", - "axios" : "^1.7.2", - "compression" : "^1.7.4", - "cors" : "^2.8.5", - "express" : "^4.19.2", - "express-validator" : "^7.1.0", - "form-data" : "^4.0.0", - "helmet" : "^7.1.0", - "http-status-codes" : "^2.3.0", - "json5" : "^2.2.3", - "jsonwebtoken" : "^9.0.2", - "morgan" : "^1.10.0", - "multer" : "^1.4.5-lts.1", - "mysql" : "^2.18.1", - "node" : "^20.11.0", - "parse-link-header" : "^2.0.0", - "semver" : "^7.6.2", - "swagger-ui-express" : "^5.0.0", - "tar-stream" : "^3.1.7", - "uuid" : "^9.0.1", - "winston" : "^3.13.0", - "zod" : "^3.23.8", + "dependencies": { + "@dotenvx/dotenvx": "^0.44.1", + "@gitbeaker/rest": "^40.0.3", + "@prisma/client": "^5.14.0", + "archiver": "^7.0.1", + "axios": "^1.7.2", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.19.2", + "express-validator": "^7.1.0", + "form-data": "^4.0.0", + "helmet": "^7.1.0", + "http-status-codes": "^2.3.0", + "json5": "^2.2.3", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "mysql": "^2.18.1", + "node": "^20.11.0", + "parse-link-header": "^2.0.0", + "semver": "^7.6.2", + "swagger-ui-express": "^5.0.0", + "tar-stream": "^3.1.7", + "uuid": "^9.0.1", + "winston": "^3.13.0", + "zod": "^3.23.8", "zod-validation-error": "^3.3.0" }, "devDependencies": { - "@redocly/cli" : "^1.13.0", - "@types/compression" : "^1.7.5", - "@types/cors" : "^2.8.17", - "@types/express" : "^4.17.21", - "@types/jsonwebtoken" : "^9.0.6", - "@types/morgan" : "^1.9.9", - "@types/multer" : "^1.4.11", - "@types/node" : "^20.12.12", - "@types/parse-link-header" : "^2.0.3", - "@types/semver" : "^7.5.8", + "@redocly/cli": "^1.13.0", + "@types/compression": "^1.7.5", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.6", + "@types/morgan": "^1.9.9", + "@types/multer": "^1.4.11", + "@types/node": "^20.12.12", + "@types/parse-link-header": "^2.0.3", + "@types/semver": "^7.5.8", "@types/swagger-ui-express": "^4.1.6", - "@types/tar-stream" : "^3.1.3", - "@types/uuid" : "^9.0.8", - "eslint" : "^8.57.0", - "genversion" : "^3.2.0", - "nodemon" : "^3.1.1", - "npm" : "^10.8.0", - "prisma" : "^5.14.0", - "tsx" : "^4.11.0", - "typescript" : "^5.4.5", - "typescript-eslint" : "^7.11.0" + "@types/tar-stream": "^3.1.3", + "@types/uuid": "^9.0.8", + "eslint": "^8.57.0", + "genversion": "^3.2.0", + "nodemon": "^3.1.1", + "npm": "^10.8.0", + "prisma": "^5.14.0", + "tsx": "^4.11.0", + "typescript": "^5.4.5", + "typescript-eslint": "^7.11.0" } } diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts index 7daaf8d..145ec4f 100644 --- a/ExpressAPI/src/managers/GitlabManager.ts +++ b/ExpressAPI/src/managers/GitlabManager.ts @@ -1,34 +1,34 @@ -import Config from '../config/Config.js'; -import { StatusCodes } from 'http-status-codes'; -import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility.js'; -import express from 'express'; -import SharedConfig from '../shared/config/SharedConfig.js'; -import { ArchiveType, CommitSchema, ExpandedUserSchema, Gitlab, MemberSchema, ProjectBadgeSchema, ProjectSchema, ReleaseSchema, RepositoryFileExpandedSchema, RepositoryFileSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; -import logger from '../shared/logging/WinstonLogger.js'; -import { AccessLevel, EditProjectOptions, ProjectVariableSchema, ProtectedBranchAccessLevel, ProtectedBranchSchema } from '@gitbeaker/core'; -import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode.js'; -import SharedGitlabManager from '../shared/managers/SharedGitlabManager.js'; -import * as fs from 'fs'; +import Config from '../config/Config.js'; +import { StatusCodes } from 'http-status-codes'; +import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility.js'; +import express from 'express'; +import SharedConfig from '../shared/config/SharedConfig.js'; +import { CommitSchema, ExpandedUserSchema, Gitlab, MemberSchema, ProjectBadgeSchema, ProjectSchema, ReleaseSchema, RepositoryFileExpandedSchema, RepositoryFileSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; +import logger from '../shared/logging/WinstonLogger.js'; +import { AccessLevel, EditProjectOptions, ProjectVariableSchema, ProtectedBranchAccessLevel, ProtectedBranchSchema } from '@gitbeaker/core'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode.js'; +import SharedGitlabManager from '../shared/managers/SharedGitlabManager.js'; +import fs from 'fs'; class GitlabManager extends SharedGitlabManager { constructor() { super(Config.gitlab.account.token); } - + getUserProfile(token: string): Promise<ExpandedUserSchema> | undefined { try { const profileApi = new Gitlab({ - host : SharedConfig.gitlab.URL, - oauthToken: token - }); - + host : SharedConfig.gitlab.URL, + oauthToken: token + }); + return profileApi.Users.showCurrentUser(); } catch ( e ) { logger.error(JSON.stringify(e)); return undefined; } } - + async getRepositoryMembers(idOrNamespace: string): Promise<Array<MemberSchema>> { try { return await this.api.ProjectMembers.all(idOrNamespace, { includeInherited: true }); @@ -37,7 +37,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async getRepositoryReleases(repoId: number): Promise<Array<ReleaseSchema>> { try { return await this.api.ProjectReleases.all(repoId); @@ -46,7 +46,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async getRepositoryLastCommit(repoId: number, branch: string = 'main'): Promise<CommitSchema | undefined> { try { const commits = await this.api.Commits.all(repoId, { @@ -54,14 +54,14 @@ class GitlabManager extends SharedGitlabManager { maxPages: 1, perPage : 1 }); - + return commits.length > 0 ? commits[0] : undefined; } catch ( e ) { logger.error(JSON.stringify(e)); return undefined; } } - + async getRepositoryCommit(repoId: number, commitSha: string): Promise<CommitSchema | undefined> { try { return await this.api.Commits.show(repoId, commitSha); @@ -70,25 +70,25 @@ class GitlabManager extends SharedGitlabManager { return undefined; } } - + async createRepository(name: string, description: string, visibility: 'public' | 'internal' | 'private', initializeWithReadme: boolean, namespace: number, sharedRunnersEnabled: boolean, wikiEnabled: boolean, importUrl: string): Promise<ProjectSchema> { try { return await this.api.Projects.create({ - name : name, - description : description, - importUrl : importUrl, - initializeWithReadme: initializeWithReadme, - namespaceId : namespace, - sharedRunnersEnabled: sharedRunnersEnabled, - visibility : visibility, - wikiAccessLevel : wikiEnabled ? 'enabled' : 'disabled' - }); + name : name, + description : description, + importUrl : importUrl, + initializeWithReadme: initializeWithReadme, + namespaceId : namespace, + sharedRunnersEnabled: sharedRunnersEnabled, + visibility : visibility, + wikiAccessLevel : wikiEnabled ? 'enabled' : 'disabled' + }); } catch ( e ) { logger.error(JSON.stringify(e)); return Promise.reject(e); } } - + async deleteRepository(repoId: number): Promise<void> { try { return await this.api.Projects.remove(repoId); @@ -97,7 +97,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async forkRepository(forkId: number, name: string, path: string, description: string, visibility: 'public' | 'internal' | 'private', namespace: number): Promise<ProjectSchema> { try { return await this.api.Projects.fork(forkId, { @@ -112,7 +112,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async editRepository(repoId: number, newAttributes: EditProjectOptions): Promise<ProjectSchema> { try { return await this.api.Projects.edit(repoId, newAttributes); @@ -121,11 +121,11 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<ProjectSchema> { return this.editRepository(repoId, { visibility: visibility }); } - + async addRepositoryMember(repoId: number, userId: number, accessLevel: Exclude<AccessLevel, AccessLevel.ADMIN>): Promise<MemberSchema> { try { return await this.api.ProjectMembers.add(repoId, userId, accessLevel); @@ -134,7 +134,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async addRepositoryVariable(repoId: number, key: string, value: string, isProtected: boolean, isMasked: boolean): Promise<ProjectVariableSchema> { try { return await this.api.ProjectVariables.create(repoId, key, value, { @@ -147,7 +147,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async addRepositoryBadge(repoId: number, linkUrl: string, imageUrl: string, name: string): Promise<ProjectBadgeSchema> { try { return await this.api.ProjectBadges.add(repoId, linkUrl, imageUrl, { @@ -158,12 +158,12 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async checkTemplateAccess(projectIdOrNamespace: string, req: express.Request, res?: express.Response): Promise<boolean> { // Get the Gitlab project and check if it have public or internal visibility try { const project: ProjectSchema = await this.getRepository(projectIdOrNamespace); - + if ( [ 'public', 'internal' ].includes(project.visibility) ) { req.session.sendResponse(res, StatusCodes.OK); return true; @@ -172,7 +172,7 @@ class GitlabManager extends SharedGitlabManager { req.session.sendResponse(res, StatusCodes.NOT_FOUND, undefined, 'Template not found', DojoStatusCode.GITLAB_TEMPLATE_NOT_FOUND); return false; } - + // Check if the user and dojo are members (with at least reporter access) of the project const members = await this.getRepositoryMembers(projectIdOrNamespace); const isUsersAtLeastReporter = { @@ -188,7 +188,7 @@ class GitlabManager extends SharedGitlabManager { } } }); - + if ( isUsersAtLeastReporter.user && isUsersAtLeastReporter.dojo ) { req.session.sendResponse(res, StatusCodes.OK); return true; @@ -197,7 +197,7 @@ class GitlabManager extends SharedGitlabManager { return false; } } - + async protectBranch(repoId: number, branchName: string, allowForcePush: boolean, allowedToMerge: ProtectedBranchAccessLevel, allowedToPush: ProtectedBranchAccessLevel, allowedToUnprotect: ProtectedBranchAccessLevel): Promise<ProtectedBranchSchema> { try { return await this.api.ProtectedBranches.protect(repoId, branchName, { @@ -211,7 +211,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async getRepositoryTree(repoId: number, recursive: boolean = true, branch: string = 'main'): Promise<Array<RepositoryTreeSchema>> { try { return await this.api.Repositories.allRepositoryTrees(repoId, { @@ -223,7 +223,7 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + async getFile(repoId: number, filePath: string, branch: string = 'main'): Promise<RepositoryFileExpandedSchema> { try { return await this.api.RepositoryFiles.show(repoId, filePath, branch); @@ -232,11 +232,11 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + private async createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { try { const gitFunction = create ? this.api.RepositoryFiles.create.bind(this.api) : this.api.RepositoryFiles.edit.bind(this.api); - + return await gitFunction(repoId, filePath, branch, fileBase64, commitMessage, { encoding : 'base64', authorName : authorName, @@ -247,15 +247,15 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - + createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { return this.createUpdateFile(true, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); } - + updateFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { return this.createUpdateFile(false, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); } - + async deleteFile(repoId: number, filePath: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<void> { try { return await this.api.RepositoryFiles.remove(repoId, filePath, branch, commitMessage, { @@ -267,22 +267,9 @@ class GitlabManager extends SharedGitlabManager { return Promise.reject(e); } } - - async exportRepository(repoId: number) : Promise<Blob>{ + + async archiveRepository(repoId: number) : Promise<Blob>{ try { - this.api.Repositories.showArchive(repoId).then(async archive => { - const buffer = Buffer.from(await archive.arrayBuffer()); - - fs.writeFile('repository-archive.tar.gz', buffer, (err) => { - if (err) { - console.error('Error saving archive:', err); - } else { - console.log('Archive saved successfully!'); - } - }); - }).catch(error => { - console.error('Error fetching archive:', error); - }); return await this.api.Repositories.showArchive(repoId); } catch ( e ) { logger.error(JSON.stringify(e)); diff --git a/ExpressAPI/src/routes/ApiRoutesManager.ts b/ExpressAPI/src/routes/ApiRoutesManager.ts index 57a4188..4616492 100644 --- a/ExpressAPI/src/routes/ApiRoutesManager.ts +++ b/ExpressAPI/src/routes/ApiRoutesManager.ts @@ -5,6 +5,7 @@ import SessionRoutes from './SessionRoutes.js'; import AssignmentRoutes from './AssignmentRoutes.js'; import GitlabRoutes from './GitlabRoutes.js'; import ExerciseRoutes from './ExerciseRoutes.js'; +import UserRoutes from './UserRoutes.js'; class AdminRoutesManager implements RoutesManager { @@ -14,6 +15,7 @@ class AdminRoutesManager implements RoutesManager { GitlabRoutes.registerOnBackend(backend); AssignmentRoutes.registerOnBackend(backend); ExerciseRoutes.registerOnBackend(backend); + UserRoutes.registerOnBackend(backend); } } diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts index cfe103c..211ddb1 100644 --- a/ExpressAPI/src/routes/AssignmentRoutes.ts +++ b/ExpressAPI/src/routes/AssignmentRoutes.ts @@ -23,7 +23,7 @@ import DojoModelsHelper from '../helpers/DojoModelsHelper.js'; import * as Gitlab from '@gitbeaker/rest'; import { GitbeakerRequestError } from '@gitbeaker/requester-utils'; import SharedConfig from '../shared/config/SharedConfig.js'; - +import archiver from 'archiver'; class AssignmentRoutes implements RoutesManager { private readonly assignmentValidator: ExpressValidator.Schema = { @@ -80,7 +80,7 @@ class AssignmentRoutes implements RoutesManager { backend.post('/assignments/:assignmentNameOrUrl/corrections', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), ParamsValidatorMiddleware.validate(this.assignmentAddCorrigeValidator), this.linkUpdateAssignmentCorrection(false).bind(this) as RequestHandler); backend.patch('/assignments/:assignmentNameOrUrl/corrections/:exerciseIdOrUrl', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), ParamsValidatorMiddleware.validate(this.assignmentUpdateCorrigeValidator), this.linkUpdateAssignmentCorrection(true).bind(this) as RequestHandler); backend.delete('/assignments/:assignmentNameOrUrl/corrections/:exerciseIdOrUrl', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.unlinkAssignmentCorrection.bind(this) as RequestHandler); - backend.get('/assignments/:assignmentNameOrUrl/export', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.exportAssignment.bind(this) as RequestHandler); + backend.get('/assignments/:assignmentNameOrUrl/export', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.exportLightAssignment.bind(this) as RequestHandler); } // Get an assignment by its name or gitlab url @@ -301,9 +301,75 @@ class AssignmentRoutes implements RoutesManager { return req.session.sendResponse(res, StatusCodes.OK); } - private async exportAssignment(req: express.Request, res: express.Response) { - const resDl = await GitlabManager.exportRepository(req.boundParams.assignment!.gitlabId); - return req.session.sendResponse(res, StatusCodes.OK, resDl); + // private async exportLightAssignment(req: express.Request, res: express.Response) { + // const resDl = await GitlabManager.archiveRepository(req.boundParams.assignment!.gitlabId).then(async archive => { + // const buffer = Buffer.from(await archive.arrayBuffer()); + + // const zipName = req.boundParams.assignment?.name.replace(/ /g, "_") + '.tar.gz'; + // fs.writeFile(zipName, buffer, (err) => { + // if (err) { + // console.error('Error saving archive:', err); + // } else { + // console.log('Archive saved successfully!'); + // } + // }); + // }).catch(error => { + // console.error('Error fetching archive:', error); + // }); + // return req.session.sendResponse(res, StatusCodes.OK, resDl); + // } + + private async exportLightAssignment(req: express.Request, res: express.Response) { + try { + const folderName = 'tmp2'; + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + // const parentDir = path.join('/tmp', `export_${timestamp}`); + const parentDir = path.join('/tmp', folderName); + fs.mkdirSync(parentDir, { recursive: true }); + + const archive = await GitlabManager.archiveRepository(req.boundParams.assignment!.gitlabId); + const buffer = Buffer.from(await archive.arrayBuffer()); + const zipName = req.boundParams.assignment?.name.replace(/ /g, "_") + '.tar.gz'; + const zipPath = path.join(parentDir, zipName); + + fs.writeFileSync(zipPath, buffer); + // fs.writeFile(zipPath, buffer, (err) => { + // if (err) { + // console.error('Error saving archive:', err); + // } else { + // console.log('Archive saved successfully!'); + // } + // }); + // console.log('Archive saved successfully!'); + + // const finalZipPath = path.join('/tmp', `export_${timestamp}.zip`); + const finalZipPath = path.join('/tmp', `${folderName}.zip`); + const output = fs.createWriteStream(finalZipPath); + const archiveZip = archiver('zip', { + zlib: { level: 9 } // Compression maximale + }); + + archiveZip.pipe(output); + archiveZip.directory(parentDir, false); + await archiveZip.finalize(); + + res.download(finalZipPath, `${folderName}.zip`, (err) => { + // res.download(finalZipPath, `export_${timestamp}.zip`, (err) => { + if (err) { + console.error('Error sending zip:', err); + res.status(500).send('Error sending zip'); + } else { + console.log('Zip sent successfully!'); + // Nettoyage des fichiers temporaires après envoi + // fs.rmSync(parentDir, { recursive: true, force: true }); + // fs.unlinkSync(finalZipPath); + } + }); + + } catch (error) { + console.error('Error exporting assignment:', error); + res.status(500).send('Error exporting assignment'); + } } } diff --git a/ExpressAPI/src/routes/UserRoutes.ts b/ExpressAPI/src/routes/UserRoutes.ts new file mode 100644 index 0000000..3755d01 --- /dev/null +++ b/ExpressAPI/src/routes/UserRoutes.ts @@ -0,0 +1,57 @@ +import { Express } from 'express-serve-static-core'; +import express, { RequestHandler } from 'express'; +import RoutesManager from '../express/RoutesManager'; +import SecurityMiddleware from '../middlewares/SecurityMiddleware'; +import SecurityCheckType from '../types/SecurityCheckType'; +import db from '../helpers/DatabaseHelper'; +import { StatusCodes } from 'http-status-codes'; + +class UserRoutes implements RoutesManager { + registerOnBackend(backend: Express): void { + backend.get('/users', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.getUsers.bind(this) as RequestHandler); + backend.patch('/users/:userId/role', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.changeRole.bind(this) as RequestHandler); + backend.get('/users/:userId/assignments', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.getUsersAssignments.bind(this) as RequestHandler); + } + + private async getUsers(req: express.Request, res: express.Response) { + const users = await db.user.findMany(); + return req.session.sendResponse(res, StatusCodes.OK, users); + } + + private async getUsersAssignments(req: express.Request, res: express.Response) { + const id = +req.params.userId; + const user = await db.user.findUnique({ + where: { + id: id, + }, + include: { + assignments: true, + + }, // Include the assignments related to the user + }); + return req.session.sendResponse(res, StatusCodes.OK, user); + } + + private async changeRole(req: express.Request, res: express.Response) { + const id = +req.params.userId; + const newRole = req.body.newRole; + // check admin + + if (id != req.session.profile.id) { + + await db.user.update({ + where : { + id : id + }, + data : { + role: newRole + } + }); + return req.session.sendResponse(res, StatusCodes.OK); + } else { + return req.session.sendResponse(res, StatusCodes.FORBIDDEN); + } + } +} + +export default new UserRoutes(); diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared index c2afa86..cb6c445 160000 --- a/ExpressAPI/src/shared +++ b/ExpressAPI/src/shared @@ -1 +1 @@ -Subproject commit c2afa861bf6306ddec79ffd465a4c7b0edcd3453 +Subproject commit cb6c4459a5b192a428844b0625d5cfb8558ca89f diff --git a/ExpressAPI/src/types/SecurityCheckType.ts b/ExpressAPI/src/types/SecurityCheckType.ts index 3a0b733..4b048b3 100644 --- a/ExpressAPI/src/types/SecurityCheckType.ts +++ b/ExpressAPI/src/types/SecurityCheckType.ts @@ -3,6 +3,7 @@ enum SecurityCheckType { ASSIGNMENT_STAFF = 'assignmentStaff', ASSIGNMENT_IS_PUBLISHED = 'assignmentIsPublished', EXERCISE_SECRET = 'exerciseSecret', + ADMIN = 'admin', } -- GitLab