Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • add_route_assignments
  • ask-user-to-delete-exercises-on-duplicates
  • bedran_exercise-list
  • jw_sonar
  • jw_sonar_backup
  • main
  • update-dependencies
  • v6.0.0
  • 2.0.0
  • 2.1.0
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 3.1.0
  • 3.1.1
  • 3.1.2
  • 3.1.3
  • 3.2.0
  • 3.3.0
  • 3.4.0
  • 3.4.1
  • 3.4.2
  • 3.5.0
  • 3.5.1
  • 3.5.2
  • 3.5.3
  • 4.0.0
  • 4.1.0
  • 5.0.0
  • 5.0.1
  • 6.0.0-dev
  • v1.0.1
32 results

Target

Select target project
No results found
Select Git revision
  • add_route_assignments
  • ask-user-to-delete-exercises-on-duplicates
  • bedran_exercise-list
  • jw_sonar
  • jw_sonar_backup
  • main
  • update-dependencies
  • v6.0.0
  • 2.0.0
  • 2.1.0
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 3.1.0
  • 3.1.1
  • 3.1.2
  • 3.1.3
  • 3.2.0
  • 3.3.0
  • 3.4.0
  • 3.4.1
  • 3.4.2
  • 3.5.0
  • 3.5.1
  • 3.5.2
  • 3.5.3
  • 4.0.0
  • 4.1.0
  • 5.0.0
  • 5.0.1
  • 6.0.0-dev
  • v1.0.1
32 results
Show changes

Commits on Source 10

15 files
+ 246
41
Compare changes
  • Side-by-side
  • Inline

Files

Original line number Original line Diff line number Diff line
@@ -11,6 +11,7 @@
                "@prisma/client": "^5.0.0",
                "@prisma/client": "^5.0.0",
                "axios": "^1.4.0",
                "axios": "^1.4.0",
                "bcryptjs": "^2.4.3",
                "bcryptjs": "^2.4.3",
                "compression": "^1.7.4",
                "cors": "^2.8.5",
                "cors": "^2.8.5",
                "dotenv": "^16.0.3",
                "dotenv": "^16.0.3",
                "express": "^4.18.2",
                "express": "^4.18.2",
@@ -24,17 +25,21 @@
                "multer": "^1.4.5-lts.1",
                "multer": "^1.4.5-lts.1",
                "mysql": "^2.18.1",
                "mysql": "^2.18.1",
                "node": "^20.2.0",
                "node": "^20.2.0",
                "parse-link-header": "^2.0.0",
                "uuid": "^9.0.0",
                "uuid": "^9.0.0",
                "winston": "^3.8.2"
                "winston": "^3.8.2",
                "yaml": "^2.3.1"
            },
            },
            "devDependencies": {
            "devDependencies": {
                "@types/bcryptjs": "^2.4.2",
                "@types/bcryptjs": "^2.4.2",
                "@types/compression": "^1.7.2",
                "@types/cors": "^2.8.13",
                "@types/cors": "^2.8.13",
                "@types/express": "^4.17.17",
                "@types/express": "^4.17.17",
                "@types/jsonwebtoken": "^9.0.2",
                "@types/jsonwebtoken": "^9.0.2",
                "@types/morgan": "^1.9.4",
                "@types/morgan": "^1.9.4",
                "@types/multer": "^1.4.7",
                "@types/multer": "^1.4.7",
                "@types/node": "^20.2.4",
                "@types/node": "^20.2.4",
                "@types/parse-link-header": "^2.0.1",
                "@types/uuid": "^9.0.2",
                "@types/uuid": "^9.0.2",
                "nodemon": "^2.0.22",
                "nodemon": "^2.0.22",
                "npm": "^9.6.7",
                "npm": "^9.6.7",
@@ -170,6 +175,15 @@
                "@types/node": "*"
                "@types/node": "*"
            }
            }
        },
        },
        "node_modules/@types/compression": {
            "version": "1.7.2",
            "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.2.tgz",
            "integrity": "sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==",
            "dev": true,
            "dependencies": {
                "@types/express": "*"
            }
        },
        "node_modules/@types/connect": {
        "node_modules/@types/connect": {
            "version": "3.4.35",
            "version": "3.4.35",
            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@@ -251,6 +265,12 @@
            "integrity": "sha512-ni5f8Xlf4PwnT/Z3f0HURc3ZSw8UyrqMqmM3L5ysa7VjHu8c3FOmIo1nKCcLrV/OAmtf3N4kFna/aJqxsfEtnA==",
            "integrity": "sha512-ni5f8Xlf4PwnT/Z3f0HURc3ZSw8UyrqMqmM3L5ysa7VjHu8c3FOmIo1nKCcLrV/OAmtf3N4kFna/aJqxsfEtnA==",
            "dev": true
            "dev": true
        },
        },
        "node_modules/@types/parse-link-header": {
            "version": "2.0.1",
            "resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.1.tgz",
            "integrity": "sha512-BrKNSrRTqn3UkMXvdVtr/znJch0PMBpEvEP8oBkxDx7eEGntuFLI+WpA5HGsNHK4SlqyhaMa+Ks0ViwyixQB5w==",
            "dev": true
        },
        "node_modules/@types/qs": {
        "node_modules/@types/qs": {
            "version": "6.9.7",
            "version": "6.9.7",
            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -603,6 +623,47 @@
                "node": "^12.20.0 || >=14"
                "node": "^12.20.0 || >=14"
            }
            }
        },
        },
        "node_modules/compressible": {
            "version": "2.0.18",
            "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
            "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
            "dependencies": {
                "mime-db": ">= 1.43.0 < 2"
            },
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/compression": {
            "version": "1.7.4",
            "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
            "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
            "dependencies": {
                "accepts": "~1.3.5",
                "bytes": "3.0.0",
                "compressible": "~2.0.16",
                "debug": "2.6.9",
                "on-headers": "~1.0.2",
                "safe-buffer": "5.1.2",
                "vary": "~1.1.2"
            },
            "engines": {
                "node": ">= 0.8.0"
            }
        },
        "node_modules/compression/node_modules/bytes": {
            "version": "3.0.0",
            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
            "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/compression/node_modules/safe-buffer": {
            "version": "5.1.2",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
        },
        "node_modules/concat-map": {
        "node_modules/concat-map": {
            "version": "0.0.1",
            "version": "0.0.1",
            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -4930,6 +4991,14 @@
                "fn.name": "1.x.x"
                "fn.name": "1.x.x"
            }
            }
        },
        },
        "node_modules/parse-link-header": {
            "version": "2.0.0",
            "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz",
            "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==",
            "dependencies": {
                "xtend": "~4.0.1"
            }
        },
        "node_modules/parseurl": {
        "node_modules/parseurl": {
            "version": "1.3.3",
            "version": "1.3.3",
            "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
            "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -5575,6 +5644,14 @@
            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
        },
        },
        "node_modules/yaml": {
            "version": "2.3.1",
            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
            "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
            "engines": {
                "node": ">= 14"
            }
        },
        "node_modules/yn": {
        "node_modules/yn": {
            "version": "3.1.1",
            "version": "3.1.1",
            "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
            "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@
        "@prisma/client"   : "^5.0.0",
        "@prisma/client"   : "^5.0.0",
        "axios"            : "^1.4.0",
        "axios"            : "^1.4.0",
        "bcryptjs"         : "^2.4.3",
        "bcryptjs"         : "^2.4.3",
        "compression"      : "^1.7.4",
        "cors"             : "^2.8.5",
        "cors"             : "^2.8.5",
        "dotenv"           : "^16.0.3",
        "dotenv"           : "^16.0.3",
        "express"          : "^4.18.2",
        "express"          : "^4.18.2",
@@ -35,17 +36,21 @@
        "multer"           : "^1.4.5-lts.1",
        "multer"           : "^1.4.5-lts.1",
        "mysql"            : "^2.18.1",
        "mysql"            : "^2.18.1",
        "node"             : "^20.2.0",
        "node"             : "^20.2.0",
        "parse-link-header": "^2.0.0",
        "uuid"             : "^9.0.0",
        "uuid"             : "^9.0.0",
        "winston"          : "^3.8.2"
        "winston"          : "^3.8.2",
        "yaml"             : "^2.3.1"
    },
    },
    "devDependencies": {
    "devDependencies": {
        "@types/bcryptjs"         : "^2.4.2",
        "@types/bcryptjs"         : "^2.4.2",
        "@types/compression"      : "^1.7.2",
        "@types/cors"             : "^2.8.13",
        "@types/cors"             : "^2.8.13",
        "@types/express"          : "^4.17.17",
        "@types/express"          : "^4.17.17",
        "@types/jsonwebtoken"     : "^9.0.2",
        "@types/jsonwebtoken"     : "^9.0.2",
        "@types/morgan"           : "^1.9.4",
        "@types/morgan"           : "^1.9.4",
        "@types/multer"           : "^1.4.7",
        "@types/multer"           : "^1.4.7",
        "@types/node"             : "^20.2.4",
        "@types/node"             : "^20.2.4",
        "@types/parse-link-header": "^2.0.1",
        "@types/uuid"             : "^9.0.2",
        "@types/uuid"             : "^9.0.2",
        "nodemon"                 : "^2.0.22",
        "nodemon"                 : "^2.0.22",
        "prisma"                  : "^5.0.0",
        "prisma"                  : "^5.0.0",
Original line number Original line Diff line number Diff line
@@ -16,7 +16,7 @@ class Config {
    };
    };


    public enonce: {
    public enonce: {
        default: { description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string };
        default: { description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string }; baseFiles: Array<string>; filename: string
    };
    };


    public exercice: {
    public exercice: {
@@ -64,7 +64,9 @@ class Config {
                visibility          : process.env.ENONCE_DEFAULT_VISIBILITY,
                visibility          : process.env.ENONCE_DEFAULT_VISIBILITY,
                wikiEnabled         : process.env.ENONCE_DEFAULT_WIKI_ENABLED.toBoolean(),
                wikiEnabled         : process.env.ENONCE_DEFAULT_WIKI_ENABLED.toBoolean(),
                template            : process.env.ENONCE_DEFAULT_TEMPLATE.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token)
                template            : process.env.ENONCE_DEFAULT_TEMPLATE.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token)
            }
            },
            baseFiles: JSON.parse(process.env.ENONCE_BASE_FILES || '[]'),
            filename : process.env.ENONCE_FILENAME || ''
        };
        };


        this.exercice = {
        this.exercice = {
Original line number Original line Diff line number Diff line
@@ -5,7 +5,7 @@ import Config from '../config/Config';
import express             from 'express';
import express             from 'express';
import ApiRequest          from '../types/ApiRequest';
import ApiRequest          from '../types/ApiRequest';
import UserManager         from '../managers/UserManager';
import UserManager         from '../managers/UserManager';
import DojoResponse        from '../shared/types/DojoResponse';
import DojoResponse        from '../shared/types/Dojo/DojoResponse';
import { User }            from '../types/DatabaseTypes';
import { User }            from '../types/DatabaseTypes';




@@ -26,6 +26,7 @@ class Session {
    async initSession(req: ApiRequest) {
    async initSession(req: ApiRequest) {
        const authorization = req.headers.authorization;
        const authorization = req.headers.authorization;
        if ( authorization ) {
        if ( authorization ) {
            if ( authorization.startsWith('Bearer ') ) {
                const jwtToken = authorization.replace('Bearer ', '');
                const jwtToken = authorization.replace('Bearer ', '');


                try {
                try {
@@ -38,9 +39,10 @@ class Session {
                } catch ( err ) { }
                } catch ( err ) { }
            }
            }
        }
        }
    }


    private static getToken(profileJson: any): string {
    private static getToken(profileJson: any): string {
        return profileJson.id === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {});
        return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {});
    }
    }


    private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoResponse<T>> {
    private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoResponse<T>> {
Original line number Original line Diff line number Diff line
@@ -12,6 +12,7 @@ import Config from '../config/Config';
import logger                from '../shared/logging/WinstonLogger';
import logger                from '../shared/logging/WinstonLogger';
import ParamsCallbackManager from '../middlewares/ParamsCallbackManager';
import ParamsCallbackManager from '../middlewares/ParamsCallbackManager';
import ApiRoutesManager      from '../routes/ApiRoutesManager';
import ApiRoutesManager      from '../routes/ApiRoutesManager';
import compression           from 'compression';




class API implements WorkerTask {
class API implements WorkerTask {
@@ -27,6 +28,7 @@ class API implements WorkerTask {
        this.backend.use(morganMiddleware); //Log API accesses
        this.backend.use(morganMiddleware); //Log API accesses
        this.backend.use(helmet()); //Help to secure express, https://helmetjs.github.io/
        this.backend.use(helmet()); //Help to secure express, https://helmetjs.github.io/
        this.backend.use(cors()); //Allow CORS requests
        this.backend.use(cors()); //Allow CORS requests
        this.backend.use(compression()); //Compress responses


        ParamsCallbackManager.register(this.backend);
        ParamsCallbackManager.register(this.backend);


Original line number Original line Diff line number Diff line
import { Prisma } from '@prisma/client';
import { Enonce } from '../types/DatabaseTypes';
import db         from '../helpers/DatabaseHelper';


class ExerciceManager {
    get(id: string, include: Prisma.ExerciceInclude | undefined = undefined): Promise<Enonce | undefined> {
        return db.exercice.findUnique({
                                          where  : {
                                              id: id
                                          },
                                          include: include
                                      });
    }
}


export default new ExerciceManager();
Original line number Original line Diff line number Diff line
@@ -8,6 +8,9 @@ import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility';
import ApiRequest        from '../types/ApiRequest';
import ApiRequest        from '../types/ApiRequest';
import GitlabUser        from '../shared/types/Gitlab/GitlabUser';
import GitlabUser        from '../shared/types/Gitlab/GitlabUser';
import GitlabRoutes      from '../shared/types/Gitlab/GitlabRoutes';
import GitlabRoutes      from '../shared/types/Gitlab/GitlabRoutes';
import GitlabTreeFile    from '../shared/types/Gitlab/GitlabTreeFile';
import parseLinkHeader   from 'parse-link-header';
import GitlabFile        from '../shared/types/Gitlab/GitlabFile';




class GitlabManager {
class GitlabManager {
@@ -145,6 +148,45 @@ class GitlabManager {


        return response.data;
        return response.data;
    }
    }

    async getRepositoryTree(repoId: number, recursive: boolean = true, branch: string = 'main'): Promise<Array<GitlabTreeFile>> {
        let address: string | undefined = this.getApiUrl(GitlabRoutes.REPOSITORY_TREE).replace('{{id}}', String(repoId));
        let params: any = {
            pagination: 'keyset',
            recursive : recursive,
            per_page  : 100,
            ref       : branch
        };

        let results: Array<GitlabTreeFile> = [];

        while ( params !== undefined ) {
            const response = await axios.get<Array<GitlabTreeFile>>(address, {
                params: params
            });

            results.push(...response.data);

            if ( 'link' in response.headers ) {
                const link = parseLinkHeader(response.headers['link']);
                params = link.next;
            } else {
                params = undefined;
            }
        }

        return results;
    }

    async getFile(repoId: number, filePath: string, branch: string = 'main'): Promise<GitlabFile> {
        const response = await axios.get<GitlabFile>(this.getApiUrl(GitlabRoutes.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
            params: {
                ref: branch
            }
        });

        return response.data;
    }
}
}




Original line number Original line Diff line number Diff line
@@ -3,6 +3,7 @@ import ApiRequest from '../types/ApiRequest';
import express         from 'express';
import express         from 'express';
import { StatusCodes } from 'http-status-codes';
import { StatusCodes } from 'http-status-codes';
import EnonceManager   from '../managers/EnonceManager';
import EnonceManager   from '../managers/EnonceManager';
import ExerciceManager from '../managers/ExerciceManager';




class ParamsCallbackManager {
class ParamsCallbackManager {
@@ -24,7 +25,8 @@ class ParamsCallbackManager {
    initBoundParams(req: ApiRequest) {
    initBoundParams(req: ApiRequest) {
        if ( !req.boundParams ) {
        if ( !req.boundParams ) {
            req.boundParams = {
            req.boundParams = {
                enonce: null
                enonce  : null,
                exercice: null
            };
            };
        }
        }
    }
    }
@@ -34,6 +36,12 @@ class ParamsCallbackManager {
            exercices: true,
            exercices: true,
            staff    : true
            staff    : true
        } ], 'enonce');
        } ], 'enonce');

        this.listenParam('exerciceId', backend, ExerciceManager.get.bind(ExerciceManager), [ {
            enonce : true,
            members: true,
            results: true
        } ], 'exercice');
    }
    }
}
}


Original line number Original line Diff line number Diff line
@@ -11,7 +11,7 @@ class SecurityMiddleware {
    check(checkIfConnected: boolean, ...checkTypes: Array<SecurityCheckType>): (req: ApiRequest, res: express.Response, next: express.NextFunction) => void {
    check(checkIfConnected: boolean, ...checkTypes: Array<SecurityCheckType>): (req: ApiRequest, res: express.Response, next: express.NextFunction) => void {
        return async (req: ApiRequest, res: express.Response, next: express.NextFunction) => {
        return async (req: ApiRequest, res: express.Response, next: express.NextFunction) => {
            if ( checkIfConnected ) {
            if ( checkIfConnected ) {
                if ( req.session.profile.id === null ) {
                if ( req.session.profile === null ) {
                    return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED);
                    return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED);
                }
                }
            }
            }
@@ -19,9 +19,9 @@ class SecurityMiddleware {
            let isAllowed = checkTypes.length === 0;
            let isAllowed = checkTypes.length === 0;


            if ( !isAllowed ) {
            if ( !isAllowed ) {
                for ( let checkType of checkTypes ) {
                for ( const checkType of checkTypes ) {
                    try {
                    try {
                        switch ( checkType ) {
                        switch ( String(checkType) ) {
                            case SecurityCheckType.TEACHING_STAFF:
                            case SecurityCheckType.TEACHING_STAFF:
                                isAllowed = isAllowed || req.session.profile.isTeachingStaff;
                                isAllowed = isAllowed || req.session.profile.isTeachingStaff;
                                break;
                                break;
@@ -31,8 +31,10 @@ class SecurityMiddleware {
                            case SecurityCheckType.ENONCE_IS_PUBLISHED:
                            case SecurityCheckType.ENONCE_IS_PUBLISHED:
                                isAllowed = isAllowed || req.boundParams.enonce.published;
                                isAllowed = isAllowed || req.boundParams.enonce.published;
                                break;
                                break;
                            case SecurityCheckType.EXERCICE_SECRET:
                                isAllowed = isAllowed || (req.headers.authorization && req.headers.authorization && req.headers.authorization.replace('ExerciceSecret ', '') === req.boundParams.exercice.secret);
                                break;
                            default:
                            default:
                                isAllowed = isAllowed || false;
                                break;
                                break;
                        }
                        }
                    } catch ( e ) {
                    } catch ( e ) {
Original line number Original line Diff line number Diff line
@@ -75,6 +75,8 @@ class EnonceRoutes implements RoutesManager {
        let repository: GitlabRepository;
        let repository: GitlabRepository;
        try {
        try {
            repository = await GitlabManager.createRepository(params.name, Config.enonce.default.description.replace('{{ENONCE_NAME}}', params.name), Config.enonce.default.visibility, Config.enonce.default.initReadme, Config.gitlab.group.enonces, Config.enonce.default.sharedRunnersEnabled, Config.enonce.default.wikiEnabled, params.template);
            repository = await GitlabManager.createRepository(params.name, Config.enonce.default.description.replace('{{ENONCE_NAME}}', params.name), Config.enonce.default.visibility, Config.enonce.default.initReadme, Config.gitlab.group.enonces, Config.enonce.default.sharedRunnersEnabled, Config.enonce.default.wikiEnabled, params.template);

            await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
        } catch ( error ) {
        } catch ( error ) {
            if ( error instanceof AxiosError ) {
            if ( error instanceof AxiosError ) {
                if ( error.response.data.message.name && error.response.data.message.name == 'has already been taken' ) {
                if ( error.response.data.message.name && error.response.data.message.name == 'has already been taken' ) {
Original line number Original line Diff line number Diff line
@@ -20,6 +20,11 @@ import { Prisma } from '@prisma/client';
import { Enonce, Exercice }      from '../types/DatabaseTypes';
import { Enonce, Exercice }      from '../types/DatabaseTypes';
import db                        from '../helpers/DatabaseHelper';
import db                        from '../helpers/DatabaseHelper';
import SecurityCheckType         from '../types/SecurityCheckType';
import SecurityCheckType         from '../types/SecurityCheckType';
import GitlabTreeFile            from '../shared/types/Gitlab/GitlabTreeFile';
import GitlabFile                from '../shared/types/Gitlab/GitlabFile';
import YAML                      from 'yaml';
import EnonceFile                from '../shared/types/Dojo/EnonceFile';
import GitlabTreeFileType        from '../shared/types/Gitlab/GitlabTreeFileType';




class ExerciceRoutes implements RoutesManager {
class ExerciceRoutes implements RoutesManager {
@@ -33,6 +38,8 @@ class ExerciceRoutes implements RoutesManager {


    registerOnBackend(backend: Express) {
    registerOnBackend(backend: Express) {
        backend.post('/enonces/:enonceNameOrUrl/exercices', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciceValidator), this.createExercice.bind(this));
        backend.post('/enonces/:enonceNameOrUrl/exercices', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciceValidator), this.createExercice.bind(this));

        backend.get('/exercices/:exerciceId/enonce', SecurityMiddleware.check(false, SecurityCheckType.EXERCICE_SECRET), this.getEnonce.bind(this));
    }
    }


    private getExerciceName(enonce: Enonce, members: Array<GitlabUser>, suffix: number): string {
    private getExerciceName(enonce: Enonce, members: Array<GitlabUser>, suffix: number): string {
@@ -58,7 +65,7 @@ class ExerciceRoutes implements RoutesManager {
            try {
            try {
                repository = await GitlabManager.forkRepository((enonce.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciceName(enonce, params.members, suffix), this.getExercicePath(req.boundParams.enonce, exerciceId), Config.exercice.default.description.replace('{{ENONCE_NAME}}', enonce.name), Config.exercice.default.visibility, Config.gitlab.group.exercices);
                repository = await GitlabManager.forkRepository((enonce.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciceName(enonce, params.members, suffix), this.getExercicePath(req.boundParams.enonce, exerciceId), Config.exercice.default.description.replace('{{ENONCE_NAME}}', enonce.name), Config.exercice.default.visibility, Config.gitlab.group.exercices);


                await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER);
                await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);


                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCICE_ID', exerciceId, false, true);
                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCICE_ID', exerciceId, false, true);
                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true);
                await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true);
@@ -127,6 +134,42 @@ class ExerciceRoutes implements RoutesManager {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
        }
        }
    }
    }

    private async getEnonce(req: ApiRequest, res: express.Response) {
        const repoTree: Array<GitlabTreeFile> = await GitlabManager.getRepositoryTree(req.boundParams.exercice.enonce.gitlabId);

        let enonceYamlFile: GitlabFile;
        let immutableFiles: Array<GitlabFile> = await Promise.all(Config.enonce.baseFiles.map(async (baseFile: string) => {
            let file = await GitlabManager.getFile(req.boundParams.exercice.enonce.gitlabId, baseFile);

            if ( baseFile === Config.enonce.filename ) {
                enonceYamlFile = file;
            }

            return file;
        }));

        const dojoEnonceFile: EnonceFile = YAML.parse(atob(enonceYamlFile.content));

        const immutablePaths = dojoEnonceFile.immutable.map(fileDescriptor => fileDescriptor.path);

        await Promise.all(repoTree.map(async gitlabTreeFile => {
            if ( gitlabTreeFile.type == GitlabTreeFileType.BLOB ) {
                for ( const immutablePath of immutablePaths ) {
                    if ( gitlabTreeFile.path.startsWith(immutablePath) ) {
                        immutableFiles.push(await GitlabManager.getFile(req.boundParams.exercice.enonce.gitlabId, gitlabTreeFile.path));
                        break;
                    }
                }
            }
        }));

        return req.session.sendResponse(res, StatusCodes.OK, {
            enonce    : (req.boundParams.exercice as Exercice).enonce,
            enonceFile: dojoEnonceFile,
            immutable : immutableFiles
        });
    }
}
}




Original line number Original line Diff line number Diff line
Subproject commit c9154d42dac81311cf1957f0d75f806737849b40
Subproject commit bfca2c401e4b5ff69b0a515fd9dcab49d36ee212
Original line number Original line Diff line number Diff line
import express              from 'express';
import express              from 'express';
import Session              from '../controllers/Session';
import Session              from '../controllers/Session';
import { Enonce } from './DatabaseTypes';
import { Enonce, Exercice } from './DatabaseTypes';




type ApiRequest = express.Request & {
type ApiRequest = express.Request & {
    session: Session, boundParams: {
    session: Session, boundParams: {
        enonce: Enonce
        enonce: Enonce, exercice: Exercice
    }
    }
}
}


Original line number Original line Diff line number Diff line
@@ -14,6 +14,7 @@ const enonceBase = Prisma.validator<Prisma.EnonceArgs>()({
                                                         });
                                                         });
const exerciceBase = Prisma.validator<Prisma.ExerciceArgs>()({
const exerciceBase = Prisma.validator<Prisma.ExerciceArgs>()({
                                                                 include: {
                                                                 include: {
                                                                     enonce : true,
                                                                     members: true,
                                                                     members: true,
                                                                     results: true
                                                                     results: true
                                                                 }
                                                                 }
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@ enum SecurityCheckType {
    TEACHING_STAFF      = 'teachingStaff',
    TEACHING_STAFF      = 'teachingStaff',
    ENONCE_STAFF        = 'enonceStaff',
    ENONCE_STAFF        = 'enonceStaff',
    ENONCE_IS_PUBLISHED = 'enonceIsPublished',
    ENONCE_IS_PUBLISHED = 'enonceIsPublished',
    EXERCICE_SECRET     = 'exerciceSecret',
}
}