diff --git a/ExpressAPI/prisma/migrations/20240603201959_add_correction_commit_to_exercise/migration.sql b/ExpressAPI/prisma/migrations/20240603201959_add_correction_commit_to_exercise/migration.sql new file mode 100644 index 0000000000000000000000000000000000000000..cf8fa0240acfd338b0414f0394685a96b8b2602b --- /dev/null +++ b/ExpressAPI/prisma/migrations/20240603201959_add_correction_commit_to_exercise/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `Exercise` ADD COLUMN `correctionCommit` JSON NULL; diff --git a/ExpressAPI/prisma/schema.prisma b/ExpressAPI/prisma/schema.prisma index 52ee5ab3cfe6fc10d85f047367dadb1058eab018..0f3a81ba5e82ca4b5127819a085a05715c0f31d3 100644 --- a/ExpressAPI/prisma/schema.prisma +++ b/ExpressAPI/prisma/schema.prisma @@ -51,6 +51,8 @@ model Exercise { gitlabLastInfo Json @db.Json gitlabLastInfoDate DateTime + correctionCommit Json? @db.Json + assignment Assignment @relation(fields: [assignmentName], references: [name], onDelete: NoAction, onUpdate: Cascade) members User[] diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts index 8809faa24c54ac9004e5489f851509c8297d205d..a0636c4bcdfc65c3a686a525dc9afe6580a1723b 100644 --- a/ExpressAPI/src/managers/GitlabManager.ts +++ b/ExpressAPI/src/managers/GitlabManager.ts @@ -286,6 +286,23 @@ class GitlabManager { }); return response.data; } + + async scheduleExportRepository(repoId : number) { + const response = await axios.post(this.getApiUrl(GitlabRoute.REPOSITORY_SCHEDULE_EXPORT).replace('{{id}}', String(repoId))); + return response.data; + } + + async exportStatusRepository(repoId : number) { + const response = await axios.get(this.getApiUrl(GitlabRoute.REPOSITORY_EXPORT_STATUS).replace('{{id}}', String(repoId))); + return response.data; + } + + async exportDownloadRepository(repoId : number) { + const response = await axios.get(this.getApiUrl(GitlabRoute.REPOSITORY_DOWNLOAD).replace('{{id}}', String(repoId)), { + responseType: 'arraybuffer', + }); + return response.data; + } } diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts index b5fbd848f7496344ec470a618252e536c4a9ccd8..47d40a218a895e3af488cb8987f088f9041c671f 100644 --- a/ExpressAPI/src/routes/AssignmentRoutes.ts +++ b/ExpressAPI/src/routes/AssignmentRoutes.ts @@ -52,6 +52,8 @@ class AssignmentRoutes implements RoutesManager { backend.patch('/assignments/:assignmentNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.unpublishAssignment.bind(this)); backend.patch('/assignments/:assignmentNameOrUrl/deleted', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.deleteAssignment.bind(this)); + backend.get('/assignments/:assignmentNameOrUrl/export', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.exportAssign.bind(this)); + backend.get('/assignments/:assignmentNameOrUrl/createSummary', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.createSummary.bind(this)); } // Get an assignment by its name or gitlab url private async getAssignment(req: express.Request, res: express.Response) { @@ -214,17 +216,17 @@ class AssignmentRoutes implements RoutesManager { private async deleteAssignment(req : express.Request, res : express.Response) { const nameAssignment = req.params.assignmentNameOrUrl; - + const repo = await db.assignment.findUnique({ where : { name : String(nameAssignment) } }); - + if (!repo) { return req.session.sendResponse(res, StatusCodes.NOT_FOUND); } - + // const members = await GitlabManager.getRepositoryMembers(String(repo['gitlabId'])); const members = await GitlabManager.getRepositoryDirectMembers(String(repo['gitlabId'])); members.forEach(async member => { @@ -242,7 +244,7 @@ class AssignmentRoutes implements RoutesManager { await GitlabManager.moveRepositorySubGroup(repo['gitlabId'], 14193); logger.debug("Repo was successfully move"); } catch (error) { - logger.debug(`Error while moving the repo to "deleted" with the error : ${error}`); + logger.debug(`Error while moving the repo to "deleted" with the error : ${error}`); } await db.assignment.update({ @@ -256,6 +258,91 @@ class AssignmentRoutes implements RoutesManager { return req.session.sendResponse(res, StatusCodes.OK); } + + // private async exportAssign(req : express.Request, res : express.Response) { + // const nameAssignment = req.params.assignmentNameOrUrl; + // let exportFinished = false; + // const repo = await db.assignment.findUnique({ + // where : { + // name : String(nameAssignment) + // } + // }); + + // if (!repo) { + // return req.session.sendResponse(res, StatusCodes.NOT_FOUND); + // } + // const repoId = repo['gitlabId']; + // try { + // const resExport = await GitlabManager.scheduleExportRepository(repoId); + // if (resExport['message'] == '202 Accepted') { + // while (!exportFinished) { + // const exportStatus = await GitlabManager.exportStatusRepository(repoId); + // if (exportStatus['export_status'] == 'finished') { + // exportFinished = true; + // const repoData = await GitlabManager.exportDownloadRepository(repo['gitlabId']); + // logger.debug(repoData); + // // fs.writeFileSync('./tmprepo.tar', repoData.data); + // return req.session.sendResponse(res, StatusCodes.OK, exportStatus['_links']['web_url']); + // } + // } + // } + // } catch (error) { + // logger.error(`Error while downloading the repo with the error : ${error}`); + // } + + // return req.session.sendResponse(res, StatusCodes.OK); + // } + + private async exportAssign(req: express.Request, res: express.Response) { + const nameAssignment = req.params.assignmentNameOrUrl; + let exportFinished = false; + + const repo = await db.assignment.findUnique({ + where: { name: String(nameAssignment) } + }); + + if (!repo) { + return req.session.sendResponse(res, StatusCodes.NOT_FOUND); + } + + const repoId = repo['gitlabId']; + + try { + const resExport = await GitlabManager.scheduleExportRepository(repoId); + if (resExport['message'] === '202 Accepted') { + while (!exportFinished) { + const exportStatus = await GitlabManager.exportStatusRepository(repoId); + + if (exportStatus['export_status'] === 'finished') { + exportFinished = true; + + const repoData = await GitlabManager.exportDownloadRepository(repoId); + logger.debug(repoData); + + if (!repoData.data || repoData.data.length === 0) { + logger.error('The downloaded export file is empty.'); + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, 'Export file is empty.'); + } + + // Écriture du fichier exporté + // fs.writeFileSync('./tmprepo.tar', repoData.data); + + return req.session.sendResponse(res, StatusCodes.OK, exportStatus['_links']['web_url']); + } else { + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } + } else { + logger.error(`Failed to initiate export: ${resExport['message']}`); + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, 'Failed to initiate export.'); + } + } catch (error) { + logger.error(`Error while downloading the repo: ${error}`); + return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, `Error while downloading the repo: ${error}`); + } + + return req.session.sendResponse(res, StatusCodes.OK); + } } diff --git a/ExpressAPI/src/shared/types/Gitlab/GitlabRoute.ts b/ExpressAPI/src/shared/types/Gitlab/GitlabRoute.ts index 4485f38e3d224e8a169e5c682a929e58424689ed..c30e4a53eb1eb03c574cc2b88159c09ce442d4db 100644 --- a/ExpressAPI/src/shared/types/Gitlab/GitlabRoute.ts +++ b/ExpressAPI/src/shared/types/Gitlab/GitlabRoute.ts @@ -19,6 +19,9 @@ enum GitlabRoute { REPOSITORY_PIPELINES = '/projects/{{id}}/pipelines', REPOSITORY_MEMBER_DELETE = '/projects/{{id}}/members/{{user_id}}', REPOSITORY_MOVE_SUBGROUP = '/projects/{{id}}/transfer', + REPOSITORY_SCHEDULE_EXPORT = '/projects/{{id}}/export', + REPOSITORY_EXPORT_STATUS = '/projects/{{id}}/export', + REPOSITORY_DOWNLOAD = '/projects/{{id}}/export/download', }