diff --git a/ExpressAPI/.env.vault b/ExpressAPI/.env.vault index c7d5592eefc4cfc377a11f6e7473fa2021dec809..0fddeea52298fa783288af4b6e389fc290d51817 100644 --- a/ExpressAPI/.env.vault +++ b/ExpressAPI/.env.vault @@ -4,8 +4,8 @@ #/--------------------------------------------------/ # development -DOTENV_VAULT_DEVELOPMENT="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" +DOTENV_VAULT_DEVELOPMENT="E+R0oGawVBBpmpk3Eb90cyOPcFWVLL14izkLrwBqRIBeKPB2i16p1GPRd1z1x8oVtNg0objA7aiAUGj59MPdt66VvhI/lmgUAQpqkZMcmlNmR2qkP3xjCTHyWvCFhGH51Kbseiq9eCjpXNLZF16Qdk6r2K8FbkT1lsenSBa1hdqNqI9kroCHAzH/rmbINv8jGa9iOiwQKQuQ1CUp1L/+RWyhOcis1rdrdyFbcArsl5Ei6fIIxWnmNffzxBfQwzIJMIZxFvuX3CRxI8moD+nyRovmsmhswGPA+J1Yiitautou4kH1alHdMZ9WV5o+B1ZqFXjisZmbVx+feeDqE8E7IthvN3VUEIezddjs9aDE4zGkmcEG37GXywF8iL4E/wrQdEvwG16qmUUDlq6ty9mzAMS58QoDsB58jjFjkZKcSl2J9TUARtVjCh4bOxnEx8NcPwSFmijZiU9hCjYf8DJVTABvs/uCQdWJPacYmbR2X/sad92HeZp1ne0dH0joe/fg/FF2kXuZT4iXzbbriBiYP6boj9H4DS5QIEAdV0ib9o+gykAanI2eqVZXFm7uuNWN/6fdq6IiCC9RC7Veg5jQTtYPlpkEasX0XtPoZnFuc6jVlE6SXaBISYAa/Mw5aNF8ETA0wwNqSOT3zCHe3rrc8466914HzDSEVG52CqmZ3tY4UhIgSEIlG7qpcRvxc02J4PyveBJv967n9tJMfXSgSBQaLf3SAYU1LTMao1hVZD3Z7j3lWpOLXV+5frkJOsr4SNvFxZNfuQyhf6GoErPLUhGA8N4/9nowVxiz6XL4vBZZ5O/9R2y/pJNO/KnPQILYw1dd/tJ8gG/zaNoQ/sp+5PmTqkbNtq263aee7uWKV6mziH/KJZVU1d1Qt5wmsjwu+K5zLG1vyA6TL4mdSOJfFjUFBn/IHTBV2RfI09xBnP+00feYX29fQr3vKRuDqEQeKfWA+EKPjSAELTz89StCFOCnFDum/h6Pi3eoc82xp6ZvAV7mNPVy1HIt6F91/PxstRerlUn+dHu5O0hlH5GTzSnvTjNbkRsW+dcsRwz+JP1EKL26svt10EljYmECb+AnHvUjqG90oEhUckBuZV1oZmXLQnY0szmWWmw2x3NDy4os+tT4pUQ166clFa0hfMDd21TwptW9sPnuL3Z3vuueeXvpqDVYDo7Q70qPBUeasxb3yltsY+tZMCrnbKhQzSIqzBk/GAlxQQFEZ0rVFOcmwzfosAl0Ov0WSt4PgN++S9lbggu5z4mc0ptkPtLZvQ5JgV0/01ErGCO59z26X4ktLwMji7VRyQbIrAFTioxyvS/jV/XdIY8b6+m8HN4fzJvfatnblTMwNLUxXZCHfRj7yjfIJxtwSScarqM5FgmXXlFTMG6U6YR4bQBBX3J4JtzX5tSFNGUmwfXxBb/wqlNawaVE0yc8P90V4/rpwjywCOLlNeGpEWzxwKpgxgJjnf8MFcs6c4UEDhzZZeTQq+65f0I2AHO4SpZtSzu52+mY9KzqnwiCGOLg94ktfGB8/j7igMn4CFzarqeCsbV27CnjkSaifxmjuE+93cxUmgCqVE/1qjlShUQhdy8xDWEAswDFr+dYv38BDVy+3eZ/uoPzYO9/ouFZVR2FzbNERAz/mjTRLVHg7CCCDzcpIJ6npVawcsRr9/bDW+6XIy5WVYjnRJWS2NsjnMnWX2txRKIn3fMVhFam9kD5yl4KxSBoiiYFxNe57M5Wr5KqoaJ1sMgqwUIa8gwiBlInESQWavryrjlWmh8bOgQ6UZ2+ipOfBk0Tq8ZkImDCHRXsy3iBBARDSUL7Lt01FBji0FQeX4bx4lY7JrIyUu8PPLl277pQ58qqzi+UNE6nBT6oTeOs18igJ2LkHIKBc4d2E4cTAhdJsX9yOXj+exPpvHFVFCcfl9RfyHSXHnXSkXJH+Qaeub2KFczgL0FWouKpvl41pmc6zko9HnnNNumbglqUZsqzqz8q2qZFwt+y6F7Iifc4V7QMZlcICk8t+/vTOdXcmGKWpJ/2EqR8r1J98biNYVk3yTLVjhs123LzsQHfkP4wT+vxouepvGKaU54oVSzMm/g3tsKw5+BnJrkA+y1dbcYQYPg71MOah9o25BsfhnJKq71iASWkLZGmdjmc16ab5ejcw6rsWeuhvh4yShGTR7jpjPuyVJPbLKmy5Ern0gEfAeqin1VJoGMgcT5LPslXraS8rhhHejwRA4uFdVI4EYQnMeL3PFACsAFAad1u/2YMPz0m8M4LtVgKR72RTDDsDCH38D0Z8cY3qfcTioMppymorDocBmimScLGs/4qIfplNj7wLoteTedT+TzMWnTZHqy/bzvKHUkZKG/Fy//PfrmjFR77PqY9/ilKdyGmh0TxfWZIfObEOlODwiW5LpHijV2/7pGQfvD7ZschHo7pmQinkA1cFK0u/fRyiBr+N0rFVihn80CT90HRST/jKFVxGkcOVKI3oP8VRzg90M2JvimwAJMkx3G6tRtYykiRX6GKFSigmNxEDQH2/WlCoKp1GY7e1iLDmgzy93l5KgWwBNbeVW/BquT/oSAzrjGdUZoBmsnpcgCIEiXSIgFCqCeVo6qm8hx1VRZL+qV8lOr0ejYhxDfGgagaO93BKXT2qZOudlbYe2zf96y2dipPejU0z5BrK5TL/aa3SrzTj62YhUjcqUqaOpGKWEWXKVRMx1+wroJvzyml4oP2DtGn+4g+7i83uY5mklXAE7tsbb7hniEJ3Tu6tkx7KLZF1Xm4CuOmUeDu0jBRkvyuh/CVcgfd8HcQgOMD4ezGIyMjJ3kLors9pYHlYQIFzrgMwPBY5z4Xs0gb/B47B7QBq42TG8/lSVoBBSSgqwFSprGo5oj+37gZMWVGsjbnrvymOvywv2q7bhs2vl7Ar9XZvalKRH1bEB4WcIq44evuw0WhsKGWf/yMgFKXywXkBomNtdxMzcE04r9Q8K5sae9B4FaEM16NfyBkfv/mBFFOhVch7HeTt09+PxUPrehw867QBRopBSRMeDji+lllgDshhQXqz6qmfDnKndt14jCXZOxEumobzz0aS0s3HUrtBtFmLe7+FTBrM8wzvg9RhbnJ/0OhopvePJE/Y+n1TSUIPf7KNd5dD4yw+HlZlYifml8QCd6wNWaRYQ6yjd7kIlToM7SWw55LBIl5MvjM81RjEI0HHB9WVkneLC1k78yQSHHBnn21znNNYayBCXZ9XRnbpfkQuipoeB4YCywWUjiQYx4G+u3C2WzUtI8lcbNfisGnzfA8uMuASe5xNuvDFVq3VEO9lmSc6ce3/bF+dVN0usopGZXxe1SAqC1h6khVSVr90FCNvHsyAYY3KEF8texB2ph1UvHr91rbS9LLXWg+Z8pKc5hEXxM=" # production -DOTENV_VAULT_PRODUCTION="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" +DOTENV_VAULT_PRODUCTION="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" diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts index 369959d7aee3655af4733916a95f5060df10002a..beab7cc8a1cc89ed8e148766c2e1207d62a1ec8d 100644 --- a/ExpressAPI/src/config/Config.ts +++ b/ExpressAPI/src/config/Config.ts @@ -5,7 +5,9 @@ import { Exercise } from '../types/DatabaseTypes'; import JSON5 from 'json5'; -type ConfigGitlabBadge = { link: string, imageUrl: string } +type ConfigGitlabBadge = { + link: string, imageUrl: string +} class Config { @@ -13,7 +15,11 @@ class Config { port: number }; - public readonly requestClientValidation: { version: { [client: string]: string } }; // { version: { CLIENT: CONDITION } } + public readonly requestClientValidation: { + version: { + [client: string]: string + } + }; // { version: { CLIENT: CONDITION } } public readonly jwtConfig: { secret: string; expiresIn: number; @@ -28,15 +34,27 @@ class Config { }; public readonly gitlab: { - urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; assignments: number; exercises: number; }, badges: { pipeline: ConfigGitlabBadge } + urls: Array<string>; repository: { + timeoutAfterCreation: number; + }; account: { + id: number; username: string; token: string; + }; group: { + root: number; templates: number; assignments: number; exercises: number; + }, badges: { + pipeline: ConfigGitlabBadge + } }; public readonly assignment: { - default: { description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string }; baseFiles: Array<string>; filename: string + default: { + description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string + }; baseFiles: Array<string>; filename: string }; public readonly exercise: { - maxSameName: number; resultsFolder: string, pipelineResultsFolder: string; default: { description: string; visibility: string; }; + maxSameName: number; resultsFolder: string, pipelineResultsFolder: string; default: { + description: string; visibility: string; + }; }; constructor() { @@ -47,8 +65,7 @@ class Config { this.requestClientValidation = JSON5.parse(process.env.REQUEST_CLIENT_VALIDATION || '{"version": {}}'); this.jwtConfig = { - secret : process.env.JWT_SECRET_KEY || '', - expiresIn: Number(process.env.SESSION_TIMEOUT || 0) + secret: process.env.JWT_SECRET_KEY || '', expiresIn: Number(process.env.SESSION_TIMEOUT || 0) }; this.login = { @@ -60,46 +77,29 @@ class Config { }; this.gitlab = { - urls : JSON5.parse(process.env.GITLAB_URLS || '[]'), - account: { - id : Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0), - username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '', - token : process.env.GITLAB_DOJO_ACCOUNT_TOKEN || '' - }, - group : { - root : Number(process.env.GITLAB_GROUP_ROOT_ID || 0), - templates : Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), - assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), - exercises : Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0) - }, - badges : { + urls : JSON5.parse(process.env.GITLAB_URLS || '[]'), account: { + id: Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0), username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '', token: process.env.GITLAB_DOJO_ACCOUNT_TOKEN || '' + }, repository: { + timeoutAfterCreation: Number(process.env.GITLAB_REPOSITORY_CREATION_TIMEOUT || 5000) + }, group : { + root: Number(process.env.GITLAB_GROUP_ROOT_ID || 0), templates: Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), exercises: Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0) + }, badges : { pipeline: { - link : process.env.GITLAB_BADGE_PIPELINE_LINK || '', - imageUrl: process.env.GITLAB_BADGE_PIPELINE_IMAGE_URL || '' + link: process.env.GITLAB_BADGE_PIPELINE_LINK || '', imageUrl: process.env.GITLAB_BADGE_PIPELINE_IMAGE_URL || '' } } }; this.assignment = { - default : { - description : process.env.ASSIGNMENT_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', - initReadme : process.env.ASSIGNMENT_DEFAULT_INIT_README?.toBoolean() ?? false, - sharedRunnersEnabled: process.env.ASSIGNMENT_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true, - visibility : process.env.ASSIGNMENT_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE, - wikiEnabled : process.env.ASSIGNMENT_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false, - template : process.env.ASSIGNMENT_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? '' - }, - baseFiles: JSON5.parse(process.env.ASSIGNMENT_BASE_FILES || '[]'), - filename : process.env.ASSIGNMENT_FILENAME || '' + default : { + description: process.env.ASSIGNMENT_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', initReadme: process.env.ASSIGNMENT_DEFAULT_INIT_README?.toBoolean() ?? false, sharedRunnersEnabled: process.env.ASSIGNMENT_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true, visibility: process.env.ASSIGNMENT_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE, wikiEnabled: process.env.ASSIGNMENT_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false, template: process.env.ASSIGNMENT_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? '' + }, baseFiles: JSON5.parse(process.env.ASSIGNMENT_BASE_FILES || '[]'), filename: process.env.ASSIGNMENT_FILENAME || '' }; this.exercise = { - maxSameName : Number(process.env.EXERCISE_MAX_SAME_NAME || 0), - resultsFolder : process.env.EXERCISE_RESULTS_FOLDER?.convertWithEnvVars() ?? '', - pipelineResultsFolder: process.env.EXERCISE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercise creation and muste be interpreted at exercise runtime - default : { - description: process.env.EXERCISE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', - visibility : process.env.EXERCISE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE + maxSameName: Number(process.env.EXERCISE_MAX_SAME_NAME || 0), resultsFolder: process.env.EXERCISE_RESULTS_FOLDER?.convertWithEnvVars() ?? '', pipelineResultsFolder: process.env.EXERCISE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercise creation and muste be interpreted at exercise runtime + default : { + description: process.env.EXERCISE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', visibility: process.env.EXERCISE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE } }; } diff --git a/ExpressAPI/src/helpers/GlobalHelper.ts b/ExpressAPI/src/helpers/GlobalHelper.ts new file mode 100644 index 0000000000000000000000000000000000000000..c97328d8e5cbc2d99b7339da2a94219e4ef01618 --- /dev/null +++ b/ExpressAPI/src/helpers/GlobalHelper.ts @@ -0,0 +1,33 @@ +import express from 'express'; +import GitlabRepository from '../shared/types/Gitlab/GitlabRepository'; +import logger from '../shared/logging/WinstonLogger'; +import GitlabManager from '../managers/GitlabManager'; +import { AxiosError } from 'axios'; +import { StatusCodes } from 'http-status-codes'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; + + +class GlobalHelper { + async repositoryCreationError(message: string, error: any, req: express.Request, res: express.Response, gitlabError: DojoStatusCode, internalError: DojoStatusCode, repositoryToRemove?: GitlabRepository): Promise<void> { + logger.error(message); + logger.error(error); + + try { + if ( repositoryToRemove ) { + await GitlabManager.deleteRepository(repositoryToRemove.id); + } + } catch ( error ) { + logger.error('Repository deletion error'); + logger.error(error); + } + + if ( error instanceof AxiosError ) { + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, `Unknown gitlab error: ${ message }`, gitlabError); + } + + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, `Unknown error: ${ message }`, internalError); + }; +} + + +export default new GlobalHelper(); \ No newline at end of file diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts index 2a89593c1e362bdd7e12106fe101be9b0c1f86c1..0c93da87efa87ca244f7a11d21d831ccb7c1bc16 100644 --- a/ExpressAPI/src/managers/GitlabManager.ts +++ b/ExpressAPI/src/managers/GitlabManager.ts @@ -84,6 +84,10 @@ class GitlabManager { return response.data; } + async deleteRepository(repoId: number): Promise<void> { + return await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_DELETE).replace('{{id}}', String(repoId))); + } + async forkRepository(forkId: number, name: string, path: string, description: string, visibility: string, namespace: number): Promise<GitlabRepository> { const response = await axios.post<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_FORK).replace('{{id}}', String(forkId)), { name : name, @@ -218,8 +222,10 @@ class GitlabManager { return response.data; } - async createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) { - await axios.post(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), { + private async createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) { + const axiosFunction = create ? axios.post : axios.put; + + await axiosFunction(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), { encoding : 'base64', branch : branch, commit_message: commitMessage, @@ -228,6 +234,14 @@ class GitlabManager { author_email : authorMail }); } + + async createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) { + return this.createUpdateFile(true, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); + } + + async updateFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) { + return this.createUpdateFile(false, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); + } } diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts index 709342bae618ff290480160efb852976eab2f63a..e4eb82bfa416c6fcfb1062b84883551c9b821988 100644 --- a/ExpressAPI/src/routes/AssignmentRoutes.ts +++ b/ExpressAPI/src/routes/AssignmentRoutes.ts @@ -23,6 +23,8 @@ import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibil import fs from 'fs'; import path from 'path'; import SharedAssignmentHelper from '../shared/helpers/Dojo/SharedAssignmentHelper'; +import GlobalHelper from '../helpers/GlobalHelper'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; class AssignmentRoutes implements RoutesManager { @@ -86,11 +88,10 @@ class AssignmentRoutes implements RoutesManager { let repository: GitlabRepository; try { repository = await GitlabManager.createRepository(params.name, Config.assignment.default.description.replace('{{ASSIGNMENT_NAME}}', params.name), Config.assignment.default.visibility, Config.assignment.default.initReadme, Config.gitlab.group.assignments, Config.assignment.default.sharedRunnersEnabled, Config.assignment.default.wikiEnabled, params.template); - - await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); - - await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status'); } catch ( error ) { + logger.error('Repo creation error'); + logger.error(error); + if ( error instanceof AxiosError ) { if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) { return res.status(StatusCodes.CONFLICT).send(); @@ -99,27 +100,32 @@ class AssignmentRoutes implements RoutesManager { return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); } - logger.error(error); return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); } + await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation)); + try { - await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); - } catch ( error ) { - logger.error(error); + await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); - if ( error instanceof AxiosError ) { - return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); - } + await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status'); + } catch ( error ) { + return GlobalHelper.repositoryCreationError('Repo params error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository); + } - return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + try { + await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); + } catch ( error ) { + return GlobalHelper.repositoryCreationError('CI file error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository); } try { await Promise.all(params.members.map(member => member.id).map(async (memberId: number): Promise<GitlabMember | false> => { try { return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER); - } catch ( e ) { + } catch ( error ) { + logger.error('Add member error'); + logger.error(error); return false; } })); @@ -150,12 +156,7 @@ class AssignmentRoutes implements RoutesManager { return req.session.sendResponse(res, StatusCodes.OK, assignment); } catch ( error ) { - if ( error instanceof AxiosError ) { - return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); - } - - logger.error(error); - return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + return GlobalHelper.repositoryCreationError('DB error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository); } } diff --git a/ExpressAPI/src/routes/ExerciseRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts index 8eab4ee47e9b6f8fcaa4605f77ed800dc72605ba..8bc2e8a43832d6d59e8fb253d6d857fad24de290 100644 --- a/ExpressAPI/src/routes/ExerciseRoutes.ts +++ b/ExpressAPI/src/routes/ExerciseRoutes.ts @@ -1,32 +1,34 @@ -import { Express } from 'express-serve-static-core'; -import express from 'express'; -import * as ExpressValidator from 'express-validator'; -import { StatusCodes } from 'http-status-codes'; -import RoutesManager from '../express/RoutesManager'; -import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware'; -import SecurityMiddleware from '../middlewares/SecurityMiddleware'; -import GitlabUser from '../shared/types/Gitlab/GitlabUser'; -import GitlabManager from '../managers/GitlabManager'; -import Config from '../config/Config'; -import GitlabRepository from '../shared/types/Gitlab/GitlabRepository'; -import { AxiosError, HttpStatusCode } from 'axios'; -import logger from '../shared/logging/WinstonLogger'; -import DojoValidators from '../helpers/DojoValidators'; -import { v4 as uuidv4 } from 'uuid'; -import GitlabMember from '../shared/types/Gitlab/GitlabMember'; -import GitlabAccessLevel from '../shared/types/Gitlab/GitlabAccessLevel'; -import { Prisma } from '@prisma/client'; -import { Assignment, Exercise } from '../types/DatabaseTypes'; -import db from '../helpers/DatabaseHelper'; -import SecurityCheckType from '../types/SecurityCheckType'; -import GitlabTreeFile from '../shared/types/Gitlab/GitlabTreeFile'; -import GitlabFile from '../shared/types/Gitlab/GitlabFile'; -import GitlabTreeFileType from '../shared/types/Gitlab/GitlabTreeFileType'; -import JSON5 from 'json5'; -import fs from 'fs'; -import path from 'path'; -import AssignmentFile from '../shared/types/Dojo/AssignmentFile'; -import ExerciseResultsFile from '../shared/types/Dojo/ExerciseResultsFile'; +import { Express } from 'express-serve-static-core'; +import express from 'express'; +import * as ExpressValidator from 'express-validator'; +import { StatusCodes } from 'http-status-codes'; +import RoutesManager from '../express/RoutesManager'; +import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware'; +import SecurityMiddleware from '../middlewares/SecurityMiddleware'; +import GitlabUser from '../shared/types/Gitlab/GitlabUser'; +import GitlabManager from '../managers/GitlabManager'; +import Config from '../config/Config'; +import GitlabRepository from '../shared/types/Gitlab/GitlabRepository'; +import { AxiosError } from 'axios'; +import logger from '../shared/logging/WinstonLogger'; +import DojoValidators from '../helpers/DojoValidators'; +import { v4 as uuidv4 } from 'uuid'; +import GitlabMember from '../shared/types/Gitlab/GitlabMember'; +import GitlabAccessLevel from '../shared/types/Gitlab/GitlabAccessLevel'; +import { Prisma } from '@prisma/client'; +import { Assignment, Exercise } from '../types/DatabaseTypes'; +import db from '../helpers/DatabaseHelper'; +import SecurityCheckType from '../types/SecurityCheckType'; +import GitlabTreeFile from '../shared/types/Gitlab/GitlabTreeFile'; +import GitlabFile from '../shared/types/Gitlab/GitlabFile'; +import GitlabTreeFileType from '../shared/types/Gitlab/GitlabTreeFileType'; +import JSON5 from 'json5'; +import fs from 'fs'; +import path from 'path'; +import AssignmentFile from '../shared/types/Dojo/AssignmentFile'; +import ExerciseResultsFile from '../shared/types/Dojo/ExerciseResultsFile'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; +import GlobalHelper from '../helpers/GlobalHelper'; class ExerciseRoutes implements RoutesManager { @@ -95,50 +97,55 @@ class ExerciseRoutes implements RoutesManager { do { try { repository = await GitlabManager.forkRepository((assignment.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciseName(assignment, params.members, suffix), this.getExercisePath(req.boundParams.assignment!, exerciseId), Config.exercise.default.description.replace('{{ASSIGNMENT_NAME}}', assignment.name), Config.exercise.default.visibility, Config.gitlab.group.exercises); - - await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); - - await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCISE_ID', exerciseId, false, true); - await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true); - await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercise.pipelineResultsFolder, false, false); - - await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status'); - break; } catch ( error ) { + logger.error('Repo creation error'); + logger.error(error); + if ( error instanceof AxiosError ) { if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) { suffix++; } else { - return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown gitlab error while forking repository', DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR); } } else { - return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while forking repository', DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR); } } } while ( suffix < Config.exercise.maxSameName ); if ( suffix >= Config.exercise.maxSameName ) { + logger.error('Max exercise with same name reached'); return res.status(StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE).send(); } + await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation)); + try { - await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercise_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); - } catch ( error ) { - logger.error(error); + await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); - if ( error instanceof AxiosError ) { - return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); - } + await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCISE_ID', exerciseId, false, true); + await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true); + await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercise.pipelineResultsFolder, false, false); - return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status'); + } catch ( error ) { + return GlobalHelper.repositoryCreationError('Repo params error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository); + } + + try { + await GitlabManager.updateFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercise_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); + } catch ( error ) { + return GlobalHelper.repositoryCreationError('CI file update error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository); } try { await Promise.all([ ...new Set([ ...assignment.staff.map(user => user.id), ...params.members.map(member => member.id) ]) ].map(async (memberId: number): Promise<GitlabMember | false> => { try { return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER); - } catch ( e ) { + } catch ( error ) { + logger.error('Add member error'); + logger.error(error); return false; } })); @@ -172,13 +179,7 @@ class ExerciseRoutes implements RoutesManager { return req.session.sendResponse(res, StatusCodes.OK, exercise); } catch ( error ) { - logger.error(error); - - if ( error instanceof AxiosError ) { - return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); - } - - return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + return GlobalHelper.repositoryCreationError('DB error', error, req, res, DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository); } } diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared index 6607858180da1c27f87f5f078e45091d45fe8caf..4a5eb68209ae9204b6d4cc8020bd62cf6a5be989 160000 --- a/ExpressAPI/src/shared +++ b/ExpressAPI/src/shared @@ -1 +1 @@ -Subproject commit 6607858180da1c27f87f5f078e45091d45fe8caf +Subproject commit 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989