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); } }