diff --git a/NodeApp/.env.vault b/NodeApp/.env.vault index 85a3476c23075cd04d21417852b32513acc4bca5..dfdcf927c6197bf63486a9fb84b97e47db10945c 100644 --- a/NodeApp/.env.vault +++ b/NodeApp/.env.vault @@ -4,11 +4,11 @@ #/--------------------------------------------------/ # development -DOTENV_VAULT_DEVELOPMENT="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" +DOTENV_VAULT_DEVELOPMENT="2wpLUByTGk2RJ96uWZuOt0Px8p3+Ixo02J0ec0EPcon4YXPoYVFVWCvDJoKbIOeqFIoNttc6mIZqg8pj90K+9WdH9C/b6bAx6Bd0dgJpALi0bpNY2ml2Ix4j4UScjuAiVZWbBtRBw3COQIGvZHx1yXgXkeectrKJdy90ItwIEodJQV4Y4vVbM/LM/0KOmt3toBJ0WLOqP9MewyrzpXM7yzS0hItBWzlGCV2If0NPS/7eqFFR3dfxqVE2a9Tx4eVa4jEiwj1rJE11I9N3D0u6x2iInYJ8yDENqVoV9iF4itpRKAUcTprN447+g4ScKWGHynj+wBM6mnXabZvMN+/iA6gC6ZXGsyt+FK3V/NSsUEMVNOQdRDnq2VZsqaV/2bPoRv5wgz3A4K55oaVl/jtMZgK86eGIPHtrH5UTjL2Zne9JwY8OV7YHF8ERQI/sFhzAqtYsCXg8q53CNreB5n5Hphenf8RsntYvMJkicV/osYqG9qqRtNk+ghP4utacFNki1BomKFoDIOR4fN3HwBxdvLqCaJ3RZnkyMfWQuxfMMFZFXi3sOZnvfmDfhqBDQniqzVkp4oZ9Jndqko05faUdYlSAO3SQX8DCutAB7pUGltiX5hHjbPk/i6Jl1HeAvq1cCmIUamVSM2actfFcmT05X31aLon1FNNZvyNlZuv5mFsK3ZF+U5iWvKgWqpY01xhaZNlLW0Bnmr3QwdbIR+DnGCfUGitIf3o9VkOTF3ElEUsVRldqA29jgXySfnBzyZVQ2TeuCzwVIEyfLquENgYeAN1/cEJqCYcEmqXiCWvPx2xFKQxfcQxxKFupabHTmbBJhblnF1Id4CGssKt8OhJbhRkydoTGkefgUxm5ioq5yKW/G5oCpE4FP/PdFKAiwPK2Hbl22hfMGXXWCEoB56+QRXK0oDAGhNNNrjhVe93bZOqUSdPQdNIp4iC2itGicGN7ps9yCScWqlv5dSUac+633YmEW6sfEbn477oz6iZZy+d58aRU3yr+Gj5ve34RtuQ39GlJ3PrxhTgl+xuKCyJgzzdGXNlpO/roZrpNVqGCpchQY8n6ahuIsbwMhPH2psxM25duu8DMNJWGGGX0hF4C6bKusQhd04YSiLWCJDYdB7OJQ+Wc/Mlcuz6/0+LOp03QgqvUm5hOqQ9Tma6ynEVpWGclSzFoHCYWNr5yneBTv28NoFU6R91+GNL26mP7CUXswl+8e7Rb1VdFK7t/CgHVlSWxeoOJJlJKSM1rYJ4NU2KKnq1oL1Asqunus7kxKEgq2rY2TIo1zYoEkEe0QRGJ/OEaPBLNGZB+PrtyxAPPi6j83aYQ9uwARO35iJVEPTIowBFgudu6iCW+kSeF40YSbNyEi9asDiWXL6oYk5VkC7lHrke+m8QZrQZjS2iK1mSMHh62SElMrWBhGpck0Qzoi5/FjvLYhWEd3q626D+d5M6MVaEjalr1FOuq1NnR2SbpwqzoGMffwgRtNhh7mhF/uh/3W886R005YB4vwYZtFEPEGUJitG9m3+b67mGAKK2qq98xLwA8RdFGoItI84Fojg9Z3LEcJ0YYdb6vnwqPVxwUOpCBsiS65i5fXJxl78A2Sh1IRleemjts3kRVIlI0TfagrwPVewmZ3AC2USFAmrFaO7beE0hJ7hRDKV33eZgW8t2lIyRzXcA8IRANhZrenlO1gFn/uTq2b93Y" # production -DOTENV_VAULT_PRODUCTION="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" +DOTENV_VAULT_PRODUCTION="ekKd8Oyzl+sLX1qB2m8q2FThDhqowPd+7bdJyREYUvT9NPni7o+wgdP1gd8nErFPKfClHm+kGNft18WxstISsMMt51T2zrdaDO0d/DGJcu7oq8c9HChJYsSeVoM+FVJYToOn1LKrgJywQPlB0krHHAYWo1vi6gnWCPb2GMU2RstBPKjQXdToLkk7u4YlNOROksMHG/n0u0n5opNFT0qTQcYeYgaTrY92I526rS/xEfJsH2gJsyPnrtNogZipwRd6ujP9fXKmS3RrQ5Sd9aadX/CqyzjchOCiUZciVsZobBuS7/RIOPWQtWmf/tXGXBZX7llUWTDOl8tryTHOIl21WmA2RxOjJjIPK8yZfn9nSNDRUD7Hd+wSgweMNHbtmbdbgpumrw0LrYWRuJIvu6sOGkLjc7zua5uxH0frEWn1h5Rjwx8OsgReEBxeoYfNPb96OF6+sayE9JitwsmznWone1GArcrrBv+aJvzr9AnwA7tq44e1WFVE/fhDaTwWraOQF0hmmA6pDJgE1gPgOiXVtgLV3J6DvPGfS8ax0ug+S8u4Pi/87vhcHud43N+CuVr5giJHSDAp8m3LCntEzF9lvkuNH57M3DHBr5YWHsvpYkmo4SIWCmVqRfsIRTeH8gCoAkxnd9PwDmJ7zJzucGEfTgzNgGavc6HHEpaW3pp2jpidp1iEmxtwbBM25umZ7P/emeztPxzPCxnAYsXov/2d6LsMCF8eNszwR0+W7lFV+JM8QrS9/KwHD9HPYNYWAJp06ludLcCKRxIbqLECZpKxjrJG9pY/7TAiuUmxiwm8FkbNYWavRoDPTcxfqTO2a76M1GfKIF+xWzqjsY7r1FkUAwCBr9CmW9ZpAx779grIyo6e7MptGIm0QHr4iH8UXFYU9S3kN/AoJUD5eTJVCa+17+hj+iyDUAcPVTDW9oJRjdZ73aZ86oui8n/1f42MkLChJJ1umxJSlvvzvH27FHWM/JHwYwLgIWC/8p/tt/Zy80XQbk8oz7nvp0C3un0lwrLJwazI4MW3Sj0E/QiKKvowX5JSOFbCOvoBreHU+U9n0veGXKqROrK2IcS2e0zExv14emiPnNrPNnoDcETwC0hGFhkJoJUiFXjnn7WAFmdMBrdxQKSelYPQ/8wAWjhgA33n1o+PkMnvkY1Yd4h8ua1WXWW1khr7nFkPv5VYDyzRWmba8Fd5JWrydDVg7QO6SYagvTdSuj6Hahe7NPL8jjaj3MNXP9yyW98tBMTm7xe8bi+kK1ZoZOcSh8DxjGFwUcQlru2RMX65zEW0q6YRNJIBnQfP7f1737wnoyWFFyKWfjGFI1n5dWycOwZn7+is1rVFPLbx45vNARRYaoJJwGKhqOdTlwM6WBeaJyYUCi6WfQ7Cfl+uHIPBvRC5zMH1Ac+wbvqqdIBc3H9XuH/30yXvM5SBCzDumzl9HY50QLUz5Q/lLrqlcllIfjdmWCuWes19oT1/Vskj5VXt7cDfWTXNPePFyECsrpNukh4SnNOfobIeNKo5+FD33hKWpSC/zTP+N0nKF/OWME9DzHO749KNweOh+W8wNZf3FKNiKARTWoM7lXnyyIY1ld7nWzpsIDtbGkncidk8cbTCmXFVQmxhYqF0DP9xCHqKKfrVdaMdirj58mOIS1KZCj9RUYYaL2ct8WhSEuNIh+Oln/UBo/6wR5+Q2itfBt4359eWb0RGl8at0pvq6OVy4bmtWbaXrW/8JwdI/QxHt9Lw9g==" # test -DOTENV_VAULT_TEST="CYDEOpA8aIlJs245eFTFon8fcioqqLopkdOco1dRb5g70jfXSvgOJQ5GYuFFGOPfZkv0Q5yTHvABPiUzMP8RS0m2x+88BKpfd2TS/9ojTq/UASfOJH0/OaR2Pfj72nKqKN3PuAE6Gf0GQsjzypa25B8z0b3eRbaV/wvCxSbV+Y2F4kkw/+mm9TtYtG3oWFHyBzcAD/zGyFcXqmRDi2tIw0IHEWzD4emIS1rJDK/8Y85xILSrALAtfyPr+a2tvSNapg8kH1dv+BG5DIZBz/8xERxJQNO68altEkpsDEoue23rGEReP3gcIfF0E7/y55PEXkPUQl2RQNXIPW9DdMa0tb94ToHiOgMmOxo6oW+liIShD6ERTZIIzcA4yD+jEEFP739evtfouEHw5SeGVp++EjWN4PwB9qmow6mNgDDQyFolxzlbRvfsWQEvTGe0urXzthE+0THbHHX128XUS3NwVWLLTZEECZRvz9ZeiuAPadKoqCdydDLg5s+ufajlmw0fFJVhMQc+AP533ttlHC2vT4xVjjN59Z2yE94kap3i4HtyGqKMq7bFJWAiLT4B615xrg9zZUarlE1qM7ZqXsGpyU3kyAm+/wf4/s2hGYl8uK/ff5rmXU4X+9gobmuV77H07iuWUJESbg3dl1NQK0X4y1zA9EWcU3OB7LFrwi3ydzgi84Aq/tvOnhxn0n1PRvMwqsX4qVj3JCynUSAM3RA8ukO9RL7CcMjNnNvHi58UhLUJVCJLyZ4MS3Hcx+GdnpQ92KQvIEUFtwcLX9SaEQSRBdRJ+Knxq711E2pJrcv1uHMi7fJbaBuPsnP/ntkcWrxqmp7EqcHl29bGzOVs9js0sjlBpnW9RjvJ0AIug3ze2JzG1qoT42jSWjVVNiVTAGGPOXIFqrzx5eaTYt2bE0oWNlQCY+0bJAPqtpBJWX2sxnnuKOtYjOYXMx7uz92WJDdtmzvXZ5ymOOs0Ye7RxxBvZUSEyBws1MCP39VuOJcMidUfQGLMhiNv7EkVaPudwYQ1pD8Fvav/oQQmRNOiIo5UrbC7dZ9fd/RrS+65mDhCxHj7nni7O959reDd1y0xo+zKmD64JChJVTWN2/YyhFdqieGL8Lmtz3Z9rkigch3q4HnpVfFJmCcBGjP/KenftIW2AWU9LMHUwwEggLTU4XyO5AhhwFzzWaYWrUqxbVxhS0gsTYw/2lm+PWXFMBI7FQgax2Y9sp/eMeKGjgkykmyftqhHfmet3C7ALzdbygpkERkq9JjXQdigtmgSP1lFxxf8LkmSdWYQqHNvrTeXDJyQdL3m2m9o0zUMBnDjtd64LnslWjUBPk7uLARd3cKIK4lPGP66+jgvMJKoGJTjFcD67xko++M+/2R/ip919uTxJ+/y1mtJXFnSL/FA4jl2UsU3YaiOISO726Zv1l4AuoKfRq6x1KlfJKemh2MQVdXwXIqvqfvOIHz6/P3Eie4cwt00WDV1GbVQgR4FEAT6uQxxMaSXTGJwi4g48TprsKN+zWmmCXzAF/Zmpy9O8GNrW9of8WY6hGe9ZpMhANhTSOIH2ey3Zenkl4ZoYKZ9ywbK6db0pSFPr9SXdzzPVOeriQzQ57ij0YX7YIZJA3YROqMRRQ+xnQTnxQlxRGTJPd5abU01KVNWy9GVu/Win7wQuWg0QeDWEIS5ga3tNb4txtWzyFam9RHVPA76YjOUIgL9NMrHq4t5iE3HAr1QU4pMZ5BEeFA/nqY7kO8rNGPYrwe41cd5RcwBajPxyGH0HGxT8ovd3U2vN6fpje6NuB4e3Uz8Go2CP4U5x/b29MoR245iPjtzhn0Q/AhIMr5MVEN8Zt8DcLBtxo2E11KQMHqXuadsqT/mS65PlshVeqgITl34IIV/tf8flC143FLQNlNECqgHgE9GLEJvuTKSHLFhDC6pHstGLEyPe8gtMikN4DlyqdZ/pqV4hhNG1E+jeL3x" +DOTENV_VAULT_TEST="b5ToqL+fFVc56n51iF2pk5fuWvjqEmTNGD7DGEzVP71lQ7KuswE3VEOCDTmNUtxpiiauF7t1/kBAozDIdSRngJz3CDPLEU61ioKZnEI9WB0EXTMyBX+ih7FiZMm+G9Qa0cVwA+g1oP2LY8TACjSxRjb0wYKTLRz6pn2J2hEqs1Fxp0UU3/zAnuIlnHwGq5gpL3S2gu5DtJULJT4uz4eI1UHI01p56R1dgW+KSor9tMHMf0PvBwa3MK3sb2EkFYf6Ith6STTXrNw49dJgnGAK7O9+V7nKiPtmC9udifUNH/i/zpDNggENiatEt6476Z+8Au0fgX+HUzC/+dlGAdDoVWwLGqGTnKbH386uuBuWtKdY+/Yvsjd7tHQY+90sv0zya28cd3daIs+ZR2YNPUbOWJ14PPkVijJs3A+MuO7WxUpGtqG4U3LL96T0tShRgf9RDeb2/d5m+YlcAYeA08cva+SBIKhPZi7HhlfY5J0sK+pNVWEZIF5hET7hZg6N2aeY1WDrKAEiayaIK5ndSBM5yiVlPmMiQkEPBDZ9U6upLhpKK57lARAYXAzkuEVRDuFuha+fcfwBsM4EvejdIG67Cu4J1IG3eq0eBybo+4jaP8p0MRgp8kwgsNjwCFPNX0H+mVH9i1t7Z3eDfocHM1WhoOEWIAPxRyj7PdrWA6StTn1HdY5SP0ytB0WSh0H+lEJ1G5uRy9K/sN8rmmGzmah10Ti+2gvrYFjyRdaZwTJXtyWN6oVZY75X3KQx+Fb2peJwTpA3vZBpo6CGUkDItO8lvxkkFKkVy1IHIyUYBSRLhuE2QAseG3q+QE9UF2cYAYtonFr+oTFqch0eDca7e4m8QsXmAC1t+opoTbpPgwyth29WHK5OARNAL+hSsGj6EMgGpJuNMFZVHhQ4KIr1JSh2MjbnYkTdWTCpLbOKEbH/40LKdKHSzg3IMQnXAl0+rtIcDUCpPXrM7miVW6+dvy9kCp4HM9+7kuHj02vDL+I9GOXsmYRXTq99G/FdRj4rKxd5/kR38fzUjuNHBuUFofqmvhGFsP6bdeNfOfKoonGwZ/9cogC9dcPysvdL2pHMZ8VWm5yaP4yzMOoo+w6VgNK93qP/NyM28b6ouUO8t3gIfnn8lKEM/vZbmSXM51eG5hD+Yxjadm0cAuDnO5R3KjsjlodtCzlAel+Gmvc+X/8jWBm6ZP0x+28rtMjStpbgt/xnoDTV1uyAFxNi72hBhbMCV8gpXtZqI9QOylBxzhWl8D8HtoS0JCxp7yVOzZK3kbQ4BFxyl+MJKElRrdOt7lhXKej30T75W+RRLujlhdI6pTVadMLDltmGU2j0YBinD2uDe1EFL1QGKTCycFLHtYTFJSLPoTCWlBO4KZNTopuWN2v4aaIDQAzs9LEhiWrA9ENhS3fBykBbE2DWY0XczeMvEEALnunrPdFTdIy9SYW2mkZwviuwPmHDbferqT/7iPnCdZY1OsT52xKr/dCJMXpxHA01yB3n+ewVtbl6/WU9Oe3coPPHBd5PctvzGhcTZ9w6DkHHzTmbHIfEBYejzc+D3y1A5VI02EAFWRiboJOht0meX5KKpPQWzOt5D2WOtdF2WkTe+Ge5sA+3w349xF9azVYuHw5qouf2+xzhyavst+4/KF6KKQCWwCU=" diff --git a/NodeApp/src/app.ts b/NodeApp/src/app.ts index 3228486f8e0531c4a9867dbb315ff1cfc21bbdc2..072d4d8c4a72bfd3dfd9683c6e1e5f5883b1b225 100644 --- a/NodeApp/src/app.ts +++ b/NodeApp/src/app.ts @@ -1,10 +1,23 @@ // ATTENTION : This line MUST be the first of this file import './init.js'; -import CommanderApp from './commander/CommanderApp.js'; -import HttpManager from './managers/HttpManager.js'; +import CommanderApp from './commander/CommanderApp.js'; +import HttpManager from './managers/HttpManager.js'; +import Config from './config/Config'; +import ConfigFiles from './config/ConfigFiles'; +import DojoBackendManager from './managers/DojoBackendManager'; +import SharedAssignmentHelper from './shared/helpers/Dojo/SharedAssignmentHelper'; -HttpManager.registerAxiosInterceptor(); +(async () => { + ConfigFiles.init(); + await Config.init(await DojoBackendManager.getApiUrl()); -(new CommanderApp()).parse(); \ No newline at end of file + SharedAssignmentHelper.init(Config.gitlabManager); + + HttpManager.registerAxiosInterceptor(); + + const commanderApp = new CommanderApp(); + await commanderApp.init(); + commanderApp.parse(); +})(); \ No newline at end of file diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts index 30e9cfe3ba8195af46113d33add1ecff63aa2550..685da94009efb76da7109860cc2d7430466073b0 100644 --- a/NodeApp/src/commander/CommanderApp.ts +++ b/NodeApp/src/commander/CommanderApp.ts @@ -1,19 +1,20 @@ import { Command, Option } from 'commander'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig.js'; import AssignmentCommand from './assignment/AssignmentCommand.js'; import ExerciseCommand from './exercise/ExerciseCommand.js'; import SharedConfig from '../shared/config/SharedConfig.js'; import boxen from 'boxen'; -import { stateConfigFile } from '../config/ConfigFiles.js'; import semver from 'semver/preload.js'; import { version } from '../config/Version.js'; -import Config from '../config/Config.js'; import CompletionCommand from './completion/CompletionCommand.js'; import AuthCommand from './auth/AuthCommand.js'; import SessionCommand from './auth/SessionCommand.js'; import UpgradeCommand from './UpgradeCommand.js'; import TextStyle from '../types/TextStyle.js'; import TagCommand from './tag/TagCommand'; +import ConfigFiles from '../config/ConfigFiles'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import Config from '../config/Config'; +import SettingsCommand from './settings/SettingsCommand'; class CommanderApp { @@ -33,7 +34,7 @@ class CommanderApp { } } - constructor() { + public async init() { this.program .name('dojo') .description('CLI of the Dojo application') @@ -95,8 +96,8 @@ ${ TextStyle.CODE(' dojo upgrade ') }`, { private informNewVersion() { if ( SharedConfig.production ) { - const latestDojoCliVersion = stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0'; - const latestDojoCliVersionNotification = stateConfigFile.getParam('latestDojoCliVersionNotification') as number | null || 0; + const latestDojoCliVersion = ConfigFiles.stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0'; + const latestDojoCliVersionNotification = ConfigFiles.stateConfigFile.getParam('latestDojoCliVersionNotification') as number | null || 0; if ( semver.lt(version as string, latestDojoCliVersion) && (new Date()).getTime() - latestDojoCliVersionNotification >= Config.versionUpdateInformationPeriodHours * 60 * 60 * 1000 ) { console.log(boxen(`The ${ latestDojoCliVersion } version of the DojoCLI is available. You can upgrade the DojoCLI by executing this command: @@ -109,7 +110,7 @@ ${ TextStyle.CODE(' dojo upgrade ') }`, { padding : 1, textAlignment : 'left' })); - stateConfigFile.setParam('latestDojoCliVersionNotification', (new Date()).getTime()); + ConfigFiles.stateConfigFile.setParam('latestDojoCliVersionNotification', (new Date()).getTime()); } } } @@ -122,6 +123,7 @@ ${ TextStyle.CODE(' dojo upgrade ') }`, { TagCommand.registerOnCommand(this.program); CompletionCommand.registerOnCommand(this.program); UpgradeCommand.registerOnCommand(this.program); + SettingsCommand.registerOnCommand(this.program); } } diff --git a/NodeApp/src/commander/UpgradeCommand.ts b/NodeApp/src/commander/UpgradeCommand.ts index 18a96ab9716fb49cdb63286c643e3d65f533d79a..653fdc8051207a4f1a6e590d07e484b1d41ef6d8 100644 --- a/NodeApp/src/commander/UpgradeCommand.ts +++ b/NodeApp/src/commander/UpgradeCommand.ts @@ -1,9 +1,9 @@ import CommanderCommand from './CommanderCommand.js'; import ora from 'ora'; -import Config from '../config/Config.js'; import TextStyle from '../types/TextStyle.js'; import os from 'os'; import { spawn } from 'child_process'; +import Config from '../config/Config'; class UpgradeCommand extends CommanderCommand { @@ -18,7 +18,7 @@ class UpgradeCommand extends CommanderCommand { private displayInstructions(upgradeCommand: string, preAlpha: boolean) { upgradeCommand = TextStyle.CODE(` ${ upgradeCommand } `); - + console.log(TextStyle.BLOCK(`You can upgrade DojoCLI on ${ os.platform() === 'darwin' ? 'macOS' : 'Linux' }:`)); ora().start().info(`By executing this command: ${ upgradeCommand }`); console.log('or'); diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts index 86a5bc585c2f68c833fb8b3618792da99fa9b439..d5cab1979c72b38b08b4ce6e6e0e2c64e7ca879a 100644 --- a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts +++ b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts @@ -6,7 +6,7 @@ import DojoBackendManager from '../../../managers/DojoBackendManager.js'; import Toolbox from '../../../shared/helpers/Toolbox.js'; import * as Gitlab from '@gitbeaker/rest'; import TextStyle from '../../../types/TextStyle.js'; -import GitlabManager from '../../../managers/GitlabManager.js'; +import Config from '../../../config/Config'; type CommandOptions = { name: string, template?: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean } @@ -35,7 +35,7 @@ class AssignmentCreateCommand extends CommanderCommand { await AccessesHelper.checkTeachingStaff(); - this.members = await GitlabManager.fetchMembers(options); + this.members = await Config.gitlabManager.fetchMembers(options); if ( !this.members ) { throw new Error(); } @@ -82,7 +82,7 @@ class AssignmentCreateCommand extends CommanderCommand { if ( options.clone ) { console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...')); - await GitlabManager.cloneRepository(options.clone, this.assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0); + await Config.gitlabManager.cloneRepository(options.clone, this.assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0); } } diff --git a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts index e2e9d67ef39b3eeab6189f28272ae9abee1ccc3c..d75bc6268e5334376c584d2706239ba3c42bbf82 100644 --- a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts +++ b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts @@ -1,6 +1,6 @@ import CommanderCommand from '../../CommanderCommand.js'; import SessionManager from '../../../managers/SessionManager.js'; -import GitlabManager from '../../../managers/GitlabManager.js'; +import Config from '../../../config/Config'; class AuthTestCommand extends CommanderCommand { @@ -14,7 +14,7 @@ class AuthTestCommand extends CommanderCommand { protected async commandAction(): Promise<void> { await SessionManager.testSession(); - await GitlabManager.testToken(); + await Config.gitlabManager.testToken(); } } diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts index 650d1504f725c7db60d9cb5b45140838d21d5300..dd3d066619e899be647a5a267e74492058934250 100644 --- a/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts @@ -1,12 +1,12 @@ import CommanderCommand from '../../CommanderCommand.js'; import ora from 'ora'; import DojoBackendManager from '../../../managers/DojoBackendManager.js'; -import Config from '../../../config/Config.js'; import Assignment from '../../../sharedByClients/models/Assignment.js'; import inquirer from 'inquirer'; import open from 'open'; import chalk from 'chalk'; import TextStyle from '../../../types/TextStyle.js'; +import Config from '../../../config/Config'; type CorrectionResume = { name: string, value: string } diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts index 3db1510582c5927fcf506ac6acc360f04548aa8c..a94277533e02a8528ee69e4951f7a827a1a86f2a 100644 --- a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts @@ -6,8 +6,8 @@ import Assignment from '../../../sharedByClients/models/Assignment.js'; import Exercise from '../../../sharedByClients/models/Exercise.js'; import * as Gitlab from '@gitbeaker/rest'; import TextStyle from '../../../types/TextStyle.js'; -import GitlabManager from '../../../managers/GitlabManager.js'; import inquirer from 'inquirer'; +import Config from '../../../config/Config'; type CommandOptions = { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean } @@ -35,7 +35,7 @@ class ExerciseCreateCommand extends CommanderCommand { await AccessesHelper.checkStudent(); - this.members = await GitlabManager.fetchMembers(options); + this.members = await Config.gitlabManager.fetchMembers(options); if ( !this.members ) { throw new Error(); } @@ -116,7 +116,7 @@ class ExerciseCreateCommand extends CommanderCommand { 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); + await Config.gitlabManager.cloneRepository(options.clone, this.exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise_${ this.exercise.assignmentName }`, true, 0); } } diff --git a/NodeApp/src/commander/settings/SettingsCommand.ts b/NodeApp/src/commander/settings/SettingsCommand.ts new file mode 100644 index 0000000000000000000000000000000000000000..6fbcb6e7a5cb0b79c7432975a9b2d12fa6a2db98 --- /dev/null +++ b/NodeApp/src/commander/settings/SettingsCommand.ts @@ -0,0 +1,23 @@ +import CommanderCommand from '../CommanderCommand.js'; +import SettingsApiCommand from './subcommands/SettingsApiCommand'; + + +class SettingsCommand extends CommanderCommand { + protected commandName: string = 'settings'; + + protected defineCommand() { + this.command + .description('manage Dojo settings'); + } + + protected defineSubCommands() { + SettingsApiCommand.registerOnCommand(this.command); + } + + protected async commandAction(): Promise<void> { + // No action + } +} + + +export default new SettingsCommand(); \ No newline at end of file diff --git a/NodeApp/src/commander/settings/subcommands/SettingsApiCommand.ts b/NodeApp/src/commander/settings/subcommands/SettingsApiCommand.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8602c0d50203a4983f102368ef1a17ec3332482 --- /dev/null +++ b/NodeApp/src/commander/settings/subcommands/SettingsApiCommand.ts @@ -0,0 +1,29 @@ +import CommanderCommand from '../../CommanderCommand.js'; +import DojoBackendManager from '../../../managers/DojoBackendManager'; +import { Option } from 'commander'; + + +class SettingsApiCommand extends CommanderCommand { + protected commandName: string = 'api'; + + protected defineCommand() { + this.command + .description('change Dojo API URL') + .option('-u, --url <string>', 'specify the url of the Dojo API') + .addOption(new Option('--clean', 'clean the Dojo API settings').conflicts('url')) + .action(this.commandAction.bind(this)); + } + + protected async commandAction(options: { url: string, clean: boolean }): Promise<void> { + if ( options.clean ) { + await DojoBackendManager.cleanApiUrl(); + } else if ( options.url ) { + await DojoBackendManager.setApiUrl(options.url); + } else { + await DojoBackendManager.askApiUrl(true); + } + } +} + + +export default new SettingsApiCommand(); \ No newline at end of file diff --git a/NodeApp/src/config/Config.ts b/NodeApp/src/config/Config.ts index 615a36942e83d11b0e63d3de6a89e8ff74944d5c..ec06f03df69177f359825ba44894ba81fa214995 100644 --- a/NodeApp/src/config/Config.ts +++ b/NodeApp/src/config/Config.ts @@ -1,19 +1,18 @@ -import getAppDataPath from 'appdata-path'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import GitlabManager from '../managers/GitlabManager'; class Config { - public readonly localConfig: { - folder: string; sessionFile: string; stateFile: string; - }; + public gitlabManager!: GitlabManager; - public readonly versionUpdateInformationPeriodHours: number; + public versionUpdateInformationPeriodHours!: number; - public readonly gitlab: { + public gitlab!: { cliReleasePage: string cliPreAlphaReleasePage: string }; - public readonly login: { + public login!: { server: { port: number, route: string }, gitlab: { @@ -23,51 +22,52 @@ class Config { } }; - public readonly folders: { + public folders!: { defaultLocalExercise: string }; - public readonly exercise: { + public exercise!: { neededFiles: Array<string> }; - public interactiveMode: boolean; + public interactiveMode!: boolean; - constructor() { - this.localConfig = { - folder : getAppDataPath('DojoCLI'), - sessionFile: process.env.LOCAL_CONFIG_FILE_SESSION || '', - stateFile : process.env.LOCAL_CONFIG_STATE || '' - }; + constructor() { } + + async init(apiUrl: string) { + await ClientsSharedConfig.init(apiUrl); + const getEnvVar = ClientsSharedConfig.envVarGetter(); + + this.gitlabManager = new GitlabManager(ClientsSharedConfig.gitlab.URL, ClientsSharedConfig.login.gitlab.client.id, ClientsSharedConfig.login.gitlab.url.redirect, ClientsSharedConfig.login.gitlab.url.token); - this.versionUpdateInformationPeriodHours = Number(process.env.VERSION_UPDATE_INFORMATION_PERIOD_HOURS || 24); + this.versionUpdateInformationPeriodHours = Number(getEnvVar('VERSION_UPDATE_INFORMATION_PERIOD_HOURS', '24')); this.gitlab = { - cliReleasePage : process.env.GITLAB_CLI_RELEASE_PAGE || '', - cliPreAlphaReleasePage: process.env.GITLAB_CLI_PREALPHA_RELEASE_PAGE || '' + cliReleasePage : getEnvVar('GITLAB_CLI_RELEASE_PAGE', ''), + cliPreAlphaReleasePage: getEnvVar('GITLAB_CLI_PREALPHA_RELEASE_PAGE', '') }; this.login = { server: { - port : Number(process.env.LOGIN_SERVER_PORT || 30992), - route: process.env.LOGIN_SERVER_ROUTE || '' + port : Number(getEnvVar('LOGIN_SERVER_PORT', '30992')), + route: getEnvVar('LOGIN_SERVER_ROUTE', '') }, gitlab: { url: { - code: process.env.LOGIN_GITLAB_URL_CODE || '' + code: getEnvVar('LOGIN_GITLAB_URL_CODE', '') } } }; this.folders = { - defaultLocalExercise: process.env.LOCAL_EXERCISE_DEFAULT_FOLDER || './' + defaultLocalExercise: getEnvVar('LOCAL_EXERCISE_DEFAULT_FOLDER', './') }; this.exercise = { - neededFiles: JSON.parse(process.env.EXERCISE_NEEDED_FILES || '[]') + neededFiles: JSON.parse(getEnvVar('EXERCISE_NEEDED_FILES', '[]')) }; - this.interactiveMode = process.env.INTERACTIVE_MODE === 'true'; + this.interactiveMode = getEnvVar('INTERACTIVE_MODE', 'false') === 'true'; } } diff --git a/NodeApp/src/config/ConfigFiles.ts b/NodeApp/src/config/ConfigFiles.ts index 9744e260e423d853a0fc25d81e106bb191e40a22..c92fb278f160ed71fb73c74b0f287e08a3ec2cce 100644 --- a/NodeApp/src/config/ConfigFiles.ts +++ b/NodeApp/src/config/ConfigFiles.ts @@ -1,9 +1,28 @@ import LocalConfigFile from './LocalConfigFile.js'; -import Config from './Config.js'; +import getAppDataPath from 'appdata-path'; -const sessionConfigFile = new LocalConfigFile(Config.localConfig.sessionFile); -const stateConfigFile = new LocalConfigFile(Config.localConfig.stateFile); +class ConfigFiles { + private localConfig: { + folder: string; sessionFile: string; stateFile: string; + }; + public sessionConfigFile!: LocalConfigFile; + public stateConfigFile!: LocalConfigFile; -export { sessionConfigFile, stateConfigFile }; \ No newline at end of file + constructor() { + this.localConfig = { + folder : getAppDataPath('DojoCLI'), + sessionFile: process.env.LOCAL_CONFIG_FILE_SESSION ?? '', + stateFile : process.env.LOCAL_CONFIG_FILE_STATE ?? '' + }; + } + + init() { + this.sessionConfigFile = new LocalConfigFile(this.localConfig.folder, this.localConfig.sessionFile); + this.stateConfigFile = new LocalConfigFile(this.localConfig.folder, this.localConfig.stateFile); + } +} + + +export default new ConfigFiles(); \ No newline at end of file diff --git a/NodeApp/src/config/LocalConfigFile.ts b/NodeApp/src/config/LocalConfigFile.ts index 29f62a6343defe952125d6e52b546fbd7f5e7057..996e6e19483fc1aad19c8317440b701cfac14105 100644 --- a/NodeApp/src/config/LocalConfigFile.ts +++ b/NodeApp/src/config/LocalConfigFile.ts @@ -1,26 +1,27 @@ import * as fs from 'fs'; -import Config from './Config.js'; import JSON5 from 'json5'; class LocalConfigFile { + private readonly folder: string; private readonly filename: string; - constructor(filename: string) { + constructor(folder: string, filename: string) { + this.folder = folder; this.filename = filename; this.loadConfig(); } private get configPath(): string { - return `${ Config.localConfig.folder }/${ this.filename }`; + return `${ this.folder }/${ this.filename }`; } private _config: { [key: string]: unknown } = {}; loadConfig() { if ( !fs.existsSync(this.configPath) ) { - fs.mkdirSync(Config.localConfig.folder, { recursive: true }); + fs.mkdirSync(this.folder, { recursive: true }); fs.writeFileSync(this.configPath, JSON5.stringify({})); } diff --git a/NodeApp/src/helpers/AccessesHelper.ts b/NodeApp/src/helpers/AccessesHelper.ts index a07b7aa24a40229be5460f1126e0e99eb4455138..cc5f2a4e2a3a838f4d9af497f47df8012e6aea06 100644 --- a/NodeApp/src/helpers/AccessesHelper.ts +++ b/NodeApp/src/helpers/AccessesHelper.ts @@ -1,5 +1,5 @@ import SessionManager from '../managers/SessionManager.js'; -import GitlabManager from '../managers/GitlabManager.js'; +import Config from '../config/Config'; class AccessesHelper { @@ -10,7 +10,7 @@ class AccessesHelper { throw new Error(); } - if ( testGitlab && !(await GitlabManager.testToken(true)).every(result => result) ) { + if ( testGitlab && !(await Config.gitlabManager.testToken(true)).every(result => result) ) { throw new Error(); } } diff --git a/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts b/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts index 235bc74664d12d874fe945f5582f652852c30568..0503c66adf4d4230ce8f2600d4e1512d96c3ef18 100644 --- a/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts +++ b/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts @@ -5,7 +5,6 @@ import AssignmentFile from '../../shared/types/Dojo/Assign import ExerciseDockerCompose from '../../sharedByClients/helpers/Dojo/ExerciseDockerCompose.js'; import ExerciseResultsSanitizerAndValidator from '../../sharedByClients/helpers/Dojo/ExerciseResultsSanitizerAndValidator.js'; import fs from 'node:fs'; -import ClientsSharedConfig from '../../sharedByClients/config/ClientsSharedConfig.js'; import SharedAssignmentHelper from '../../shared/helpers/Dojo/SharedAssignmentHelper.js'; import path from 'path'; import ExerciseCheckerError from '../../shared/types/Dojo/ExerciseCheckerError.js'; @@ -15,6 +14,7 @@ import util from 'util'; import { exec } from 'child_process'; import SharedConfig from '../../shared/config/SharedConfig.js'; import TextStyle from '../../types/TextStyle.js'; +import ClientsSharedConfig from '../../sharedByClients/config/ClientsSharedConfig'; const execAsync = util.promisify(exec); diff --git a/NodeApp/src/helpers/GlobalHelper.ts b/NodeApp/src/helpers/GlobalHelper.ts index d79317fd487a954157d4287071daaf8b345f578a..dc9d2442afe43d4e9b16d077fa3c0dd17ad3abf5 100644 --- a/NodeApp/src/helpers/GlobalHelper.ts +++ b/NodeApp/src/helpers/GlobalHelper.ts @@ -1,10 +1,10 @@ import { Command, Option } from 'commander'; -import Config from '../config/Config.js'; import SessionManager from '../managers/SessionManager.js'; import TextStyle from '../types/TextStyle.js'; import ora from 'ora'; import DojoBackendManager from '../managers/DojoBackendManager.js'; import Assignment from '../sharedByClients/models/Assignment.js'; +import Config from '../config/Config'; class GlobalHelper { diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index 0a878367581dbd7022567590557cf88222c05d00..50c2c0e01201345dd26e8f7d5375e24b20d0c881 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.js'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig.js'; import Assignment from '../sharedByClients/models/Assignment.js'; import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse.js'; import Exercise from '../sharedByClients/models/Exercise.js'; @@ -14,10 +13,121 @@ import GitlabPipelineStatus from '../shared/types/Gitlab/GitlabPipelineStatus.j import Tag from '../sharedByClients/models/Tag'; import TagProposal from '../sharedByClients/models/TagProposal'; import Result from '../sharedByClients/models/Result'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import inquirer from 'inquirer'; +import SharedConfig from '../shared/config/SharedConfig'; +import ConfigFiles from '../config/ConfigFiles'; class DojoBackendManager { - private handleApiError(error: unknown, spinner: ora.Ora, verbose: boolean, defaultErrorMessage?: string, otherErrorHandler?: (error: AxiosError, spinner: ora.Ora, verbose: boolean) => void) { + readonly API_URL_CONFIG_KEY = 'apiUrl'; + + public async getApiUrl(): Promise<string> { + //Read the config file to get the api url. If there is no api url, then ask the user to provide one. + return ConfigFiles.stateConfigFile.getParam(this.API_URL_CONFIG_KEY) as string | null || await this.askApiUrl(); + } + + public async cleanApiUrl(): Promise<void> { + const cleanApiSpinner: ora.Ora = ora('Cleaning Api URL...').start(); + ConfigFiles.stateConfigFile.setParam(this.API_URL_CONFIG_KEY, null); + cleanApiSpinner.succeed('API URL successfully cleaned'); + } + + /** + * Check if the given api url is valid and save it in the config file if it is. + * @param apiUrl + * @returns {Promise<boolean>} true if the api url is valid, false otherwise + */ + public async setApiUrl(apiUrl: string): Promise<boolean> { + let isApiUrlValid: boolean = false; + + const testApiSpinner: ora.Ora = ora('Testing Api URL...').start(); + try { + isApiUrlValid = (await axios.get<DojoBackendResponse<unknown>>(`${ apiUrl }${ ApiRoute.CLIENTS_CONFIG }`)).status === 200; + } catch ( e ) { + isApiUrlValid = false; + } + + if ( isApiUrlValid ) { + // End step: Store the preference in the config file + ConfigFiles.stateConfigFile.setParam(this.API_URL_CONFIG_KEY, apiUrl); + } + + isApiUrlValid ? testApiSpinner.succeed('API URL successfully saved') : testApiSpinner.fail('The API URL is invalid'); + + return isApiUrlValid; + } + + public async askApiUrl(showClean: boolean = false): Promise<string> { + const presets: Array<{ name: string, value: string } | inquirer.Separator> = [ { + name : 'HEPIA', + value: 'https://rdps.hesge.ch/dojo/api' + }, { + name : 'Other', + value: 'other' + } ]; + + if ( !SharedConfig.production ) { + presets.unshift({ + name : 'DEV', + value: 'http://localhost:30993' + }, { + name : 'TEST', + value: 'https://dojo-test.edu.hesge.ch/dojo/api' + }, new inquirer.Separator()); + } + + if ( showClean ) { + presets.push(new inquirer.Separator(), { + name : 'Clean API settings', + value: 'clean' + }); + } + + presets.push(new inquirer.Separator(), { + name : 'Quit', + value: 'quit' + }); + + let apiUrl: string = ''; + let isApiUrlValid: boolean = false; + + do { + // First step: Propose some presets with inquirer + apiUrl = (await inquirer.prompt({ + name : 'apiUrl', + message : 'Which API do you want to use?', + type : 'list', + pageSize: 1000, + choices : presets, + default : SharedConfig.production ? 'other' : 'http://localhost:30993' + })).apiUrl; + + + // Second step: If the user chooses other, then ask for the url of the api + switch ( apiUrl ) { + case 'other': + apiUrl = (await inquirer.prompt({ + name : 'apiUrl', + message: 'Please provide the URL of the API', + type : 'input' + })).apiUrl; + break; + case 'clean': + await this.cleanApiUrl(); + return ''; + case 'quit': + process.exit(0); + } + + // Third step: Test the api url + isApiUrlValid = await this.setApiUrl(apiUrl); + } while ( !isApiUrlValid ); + + return apiUrl; + } + + private async handleApiError(error: unknown, spinner: ora.Ora, verbose: boolean, defaultErrorMessage?: string, otherErrorHandler?: (error: AxiosError, spinner: ora.Ora, verbose: boolean) => void) { const unknownErrorMessage: string = 'unknown error'; if ( verbose ) { @@ -129,7 +239,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Template error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Template error: ${ error }`); return false; } @@ -154,7 +264,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Assignment creation error: unknown error`); + await this.handleApiError(error, spinner, verbose, `Assignment creation error: unknown error`); throw error; } @@ -176,7 +286,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Exercise creation error: unknown error`); + await this.handleApiError(error, spinner, verbose, `Exercise creation error: unknown error`); throw error; } @@ -198,7 +308,7 @@ class DojoBackendManager { return; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`); throw error; } @@ -230,7 +340,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Correction ${ isUpdate ? 'update' : 'link' } error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Correction ${ isUpdate ? 'update' : 'link' } error: ${ error }`); return false; } @@ -255,7 +365,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Correction unlink error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Correction unlink error: ${ error }`); return false; } @@ -280,7 +390,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag creation error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag creation error: ${ error }`); return undefined; } @@ -302,7 +412,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag deletion error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag deletion error: ${ error }`); return false; } @@ -335,7 +445,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag proposal creation error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag proposal creation error: ${ error }`); return undefined; } @@ -360,7 +470,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag proposal answer error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag proposal answer error: ${ error }`); return false; } @@ -405,7 +515,7 @@ class DojoBackendManager { spinner.succeed(`Exercise deleted with success`); } } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Exercise deleting error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Exercise deleting error: ${ error }`); throw error; } diff --git a/NodeApp/src/managers/GitlabManager.ts b/NodeApp/src/managers/GitlabManager.ts index 0e6c92e364f91e6cfe1be20508acd20dd2aee73b..a39a26e477e2f2c532411344fdbf11ac7e11ff2f 100644 --- a/NodeApp/src/managers/GitlabManager.ts +++ b/NodeApp/src/managers/GitlabManager.ts @@ -11,8 +11,8 @@ type getGitlabUser = (param: number | string) => Promise<UserSchema | undefined> class GitlabManager extends SharedGitlabManager { - constructor() { - super('', GlobalHelper.refreshGitlabTokenFunction.bind(GlobalHelper)); + constructor(public gitlabUrl: string, clientId?: string, urlRedirect?: string, urlToken?: string) { + super(gitlabUrl, '', clientId, urlRedirect, urlToken, GlobalHelper.refreshGitlabTokenFunction.bind(GlobalHelper)); } public async testToken(verbose: boolean = true): Promise<[ boolean, boolean ]> { @@ -213,4 +213,4 @@ class GitlabManager extends SharedGitlabManager { } -export default new GitlabManager(); +export default GitlabManager; diff --git a/NodeApp/src/managers/HttpManager.ts b/NodeApp/src/managers/HttpManager.ts index 760f19c1f1f0b4d12089bd6f9badd84e68eb424d..ce1bc53e8c7a792b9c1c5c179fe894eecb0af788 100644 --- a/NodeApp/src/managers/HttpManager.ts +++ b/NodeApp/src/managers/HttpManager.ts @@ -2,13 +2,13 @@ import axios, { AxiosError, AxiosRequestHeaders } from 'axios'; import SessionManager from './SessionManager.js'; import FormData from 'form-data'; import { StatusCodes } from 'http-status-codes'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig.js'; import { version } from '../config/Version.js'; import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse.js'; import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode.js'; import boxen from 'boxen'; -import { stateConfigFile } from '../config/ConfigFiles.js'; import TextStyle from '../types/TextStyle.js'; +import ConfigFiles from '../config/ConfigFiles'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; class HttpManager { @@ -25,9 +25,7 @@ class HttpManager { config.headers = { ...config.headers, ...config.data.getHeaders() } as AxiosRequestHeaders; } - if ( config.url && (config.url.indexOf(ClientsSharedConfig.apiURL) !== -1) ) { - - + if ( config.url && config.url.indexOf(ClientsSharedConfig.apiURL) !== -1 ) { config.headers['Accept-Encoding'] = 'gzip'; if ( config.data && Object.keys(config.data as { [key: string]: unknown }).length > 0 ) { @@ -106,11 +104,11 @@ class HttpManager { if ( response.headers['dojocli-latest-version'] ) { const latestDojoCliVersion = response.headers['dojocli-latest-version']; - const storedLatestDojoCliVersion = stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0'; + const storedLatestDojoCliVersion = ConfigFiles.stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0'; if ( latestDojoCliVersion !== storedLatestDojoCliVersion ) { - stateConfigFile.setParam('latestDojoCliVersion', latestDojoCliVersion); - stateConfigFile.setParam('latestDojoCliVersionNotification', 0); + ConfigFiles.stateConfigFile.setParam('latestDojoCliVersion', latestDojoCliVersion); + ConfigFiles.stateConfigFile.setParam('latestDojoCliVersionNotification', 0); } } diff --git a/NodeApp/src/managers/SessionManager.ts b/NodeApp/src/managers/SessionManager.ts index 7dd62ccf351bf0cf66acc08f17f907c7a6adfcfb..cffc6b979e3f1aa2e43f64a2944334461ea83865 100644 --- a/NodeApp/src/managers/SessionManager.ts +++ b/NodeApp/src/managers/SessionManager.ts @@ -7,21 +7,19 @@ import ora from 'ora'; import Permissions from '../types/Permissions.js'; import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute.js'; import DojoBackendManager from './DojoBackendManager.js'; -import Config from '../config/Config.js'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig.js'; import DojoGitlabCredentials from '../sharedByClients/types/Dojo/DojoGitlabCredentials.js'; import * as http from 'http'; import EventEmitter from 'events'; -import SharedConfig from '../shared/config/SharedConfig.js'; import chalk from 'chalk'; import inquirer from 'inquirer'; import GitlabToken from '../shared/types/Gitlab/GitlabToken.js'; import open from 'open'; -import { sessionConfigFile } from '../config/ConfigFiles.js'; import TextStyle from '../types/TextStyle.js'; import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHelper.js'; -import GitlabManager from './GitlabManager.js'; import { StatusCodes } from 'http-status-codes'; +import ConfigFiles from '../config/ConfigFiles'; +import Config from '../config/Config'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; class LoginServer { @@ -82,7 +80,7 @@ class SessionManager { } get apiToken(): string { - const apisToken = sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string }; + const apisToken = ConfigFiles.sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string }; if ( apisToken !== null && ClientsSharedConfig.apiURL in apisToken ) { return apisToken[ClientsSharedConfig.apiURL]; @@ -92,12 +90,12 @@ class SessionManager { } set apiToken(token: string) { - let apisToken = sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string }; + let apisToken = ConfigFiles.sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string }; if ( apisToken === null ) { apisToken = {}; } apisToken[ClientsSharedConfig.apiURL] = token; - sessionConfigFile.setParam(LocalConfigKeys.APIS_TOKEN, apisToken); + ConfigFiles.sessionConfigFile.setParam(LocalConfigKeys.APIS_TOKEN, apisToken); try { const payload = jwt.decode(token); @@ -111,14 +109,14 @@ class SessionManager { } get gitlabCredentials(): DojoGitlabCredentials { - return sessionConfigFile.getParam(LocalConfigKeys.GITLAB) as DojoGitlabCredentials; + return ConfigFiles.sessionConfigFile.getParam(LocalConfigKeys.GITLAB) as DojoGitlabCredentials; } set gitlabCredentials(credentials: DojoGitlabCredentials) { - sessionConfigFile.setParam(LocalConfigKeys.GITLAB, credentials); + ConfigFiles.sessionConfigFile.setParam(LocalConfigKeys.GITLAB, credentials); if ( credentials.accessToken ) { - GitlabManager.setToken(credentials.accessToken); + Config.gitlabManager.setToken(credentials.accessToken); } } @@ -127,7 +125,7 @@ class SessionManager { console.log(`${ indent }Please open the following URL in your web browser and accept to give the requested permissions to Dojo:`); console.log(TextStyle.URL(`${ indent }${ Config.login.gitlab.url.code }`)); console.log(`${ indent }Then, copy the code at the end of the redirected url and paste it bellow.`); - console.log(`${ indent }Example of url (here the code is 123456): ${ TextStyle.URL(SharedConfig.login.gitlab.url.redirect + '?code=') }${ chalk.green('123456') }`); + console.log(`${ indent }Example of url (here the code is 123456): ${ TextStyle.URL(ClientsSharedConfig.login.gitlab.url.redirect + '?code=') }${ chalk.green('123456') }`); return (await inquirer.prompt({ type : 'password', name : 'code', @@ -194,7 +192,7 @@ class SessionManager { throw error; } - ora(`Login with Gitlab (${ SharedConfig.gitlab.URL }):`).start().info(); + ora(`Login with Gitlab (${ ClientsSharedConfig.gitlab.URL }):`).start().info(); let gitlabCode: string; if ( !headless ) { @@ -209,7 +207,7 @@ class SessionManager { }).start(); let gitlabTokens: GitlabToken; try { - gitlabTokens = await GitlabManager.getTokens(gitlabCode); + gitlabTokens = await Config.gitlabManager.getTokens(gitlabCode); this.gitlabCredentials = { refreshToken: gitlabTokens.refresh_token, accessToken : gitlabTokens.access_token @@ -220,7 +218,7 @@ class SessionManager { throw error; } - const isGitlabTokensValid = (await GitlabManager.testToken()).every(value => value); + const isGitlabTokensValid = (await Config.gitlabManager.testToken()).every(value => value); if ( !isGitlabTokensValid ) { throw new Error('Gitlab tokens are invalid'); } diff --git a/NodeApp/src/shared b/NodeApp/src/shared index bf75a99ba472386daa111c2fefbe69a4272ef48c..d9ecaee5f58b1b94413903ebf737b4d235bc2858 160000 --- a/NodeApp/src/shared +++ b/NodeApp/src/shared @@ -1 +1 @@ -Subproject commit bf75a99ba472386daa111c2fefbe69a4272ef48c +Subproject commit d9ecaee5f58b1b94413903ebf737b4d235bc2858 diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients index 4e33e70f6035898f119369ae5db784d51d8298a0..64dc406659114f3afcbc89258afe84c8553c6b8f 160000 --- a/NodeApp/src/sharedByClients +++ b/NodeApp/src/sharedByClients @@ -1 +1 @@ -Subproject commit 4e33e70f6035898f119369ae5db784d51d8298a0 +Subproject commit 64dc406659114f3afcbc89258afe84c8553c6b8f