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
  • jw_sonar
  • v6.0.0
  • bedran_exercise-list
  • ask-user-to-delete-exercises-on-duplicates
  • update-dependencies
  • main
  • jw_sonar_backup
  • add_route_assignments
  • 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
  • Dojo_Project_Nguyen / Backend / DojoBackendAPI
  • Dojo Project (HES-SO) / Projects / Backend / DojoBackendAPI
2 results
Select Git revision
  • jw_sonar
  • v6.0.0
  • bedran_exercise-list
  • ask-user-to-delete-exercises-on-duplicates
  • update-dependencies
  • main
  • jw_sonar_backup
  • add_route_assignments
  • 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 5

21 files
+ 1323
72
Compare changes
  • Side-by-side
  • Inline

Files

+13 −0
Original line number Diff line number Diff line
@@ -55,12 +55,25 @@ variables:


stages:
    - code_quality
    - test
    - clean
    - upload
    - release


code_quality:lint:
    stage: code_quality
    tags:
        - code_quality
    image: node:latest
    script:
        - cd "${PROJECT_FOLDER}"

        - npm install
        - npm run lint


test:build:
    stage: test
    image: node:latest
+11 −1
Original line number Diff line number Diff line
@@ -17,7 +17,17 @@
- No modifications / Keep major and minors versions in sync with all parts of the project
-->

## 3.0.0 (?)

## 3.1.0 (???)

### 🔨 Internal / Developers
- **Typescript**: Add linter (ESLint)

### 📚 Documentation
- **API**: Routes documentation with Swagger


## 3.0.0 (2023-11-03)

### ✨ Feature
- Login to Dojo app via Gitlab OAuth
+4 −0
Original line number Diff line number Diff line
dist
node_modules
logs
prisma
 No newline at end of file
+11 −0
Original line number Diff line number Diff line
{
    "root"   : true,
    "parser" : "@typescript-eslint/parser",
    "plugins": [
        "@typescript-eslint"
    ],
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended"
    ]
}
 No newline at end of file
Compare 4d703a2d to ffc5d65f
Original line number Diff line number Diff line
Subproject commit 4d703a2dd39ec0c2b71bbbbda8900588c4e360bd
Subproject commit ffc5d65f9f0f0e825688177425e526131aa84631
+6 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="JavaScriptLibraryMappings">
    <includedPredefinedLibrary name="Node.js Core" />
  </component>
</project>
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="EslintConfiguration">
    <option name="fix-on-save" value="true" />
  </component>
</project>
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -9,5 +9,5 @@
    "verbose": true,
    "ext"    : ".ts,.js",
    "ignore" : [],
    "exec"   : "ts-node --files ./src/app.ts"
    "exec"   : "npm run lint; ts-node --files ./src/app.ts"
}
+1193 −7

File changed.

Preview size limit exceeded, changes collapsed.

+22 −19
Original line number Diff line number Diff line
{
    "name"           : "dojo_backend_api",
    "description"    : "Backend API of the Dojo project",
    "version"        : "3.0.1",
    "version"        : "3.1.0",
    "license"        : "AGPLv3",
    "author"         : "Michaël Minelli <dojo@minelli.me>",
    "main"           : "dist/src/app.js",
    "scripts"        : {
        "clean"             : "rm -R dist/*",
        "dotenv:build"      : "npx dotenv-vault local build",
        "lint"              : "npx eslint .",
        "genversion"        : "npx genversion -s -e src/config/Version.ts",
        "build"             : "npm run genversion; npx prisma generate && npx tsc --project ./ && cp -R assets dist/assets",
        "database:migrate"  : "npx prisma migrate deploy",
@@ -58,6 +59,8 @@
        "@types/semver"                   : "^7.5.3",
        "@types/tar-stream"               : "^2.2.2",
        "@types/uuid"                     : "^9.0.2",
        "@typescript-eslint/eslint-plugin": "^6.10.0",
        "@typescript-eslint/parser"       : "^6.10.0",
        "dotenv-vault"                    : "^1.25.0",
        "genversion"                      : "^3.1.1",
        "nodemon"                         : "^3.0.1",
+5 −5
Original line number Diff line number Diff line
import path    from 'node:path';
import cluster from 'node:cluster';
import myEnv = require('dotenv');
import dotenvExpand = require('dotenv-expand');


if ( cluster.isPrimary ) {
    if ( process.env.NODE_ENV && process.env.NODE_ENV === 'production' ) {
        const myEnv = require('dotenv').config();
        require('dotenv-expand').expand(myEnv);
        dotenvExpand.expand(myEnv.config());
    } else {
        require('dotenv').config({ path: path.join(__dirname, '../.env.keys') });
        const myEnv = require('dotenv').config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT });
        require('dotenv-expand').expand(myEnv);
        myEnv.config({ path: path.join(__dirname, '../.env.keys') });
        dotenvExpand.expand(myEnv.config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT }));
    }
}

+4 −4
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ class Session {
        }
    }

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

@@ -52,7 +52,7 @@ class Session {

        try {
            reasonPhrase = getReasonPhrase(code);
        } catch {}
        } catch { /* empty */ }

        return {
            timestamp   : (new Date()).toISOString(),
@@ -67,8 +67,8 @@ class Session {
     Send a response to the client
     Information: Data could be a promise or an object. If it's a promise, we wait on the data to be resolved before sending the response
     */
    sendResponse(res: express.Response, code: number, data?: any, descriptionOverride?: string, internalCode?: number) {
        Promise.resolve(data).then((toReturn: any) => {
    sendResponse(res: express.Response, code: number, data?: unknown, descriptionOverride?: string, internalCode?: number) {
        Promise.resolve(data).then((toReturn: unknown) => {
            this.getResponse(internalCode ?? code, toReturn, descriptionOverride).then(response => {
                res.status(code).json(response);
            });
+13 −8
Original line number Diff line number Diff line
@@ -5,13 +5,14 @@ import { BailOptions, ValidationChain } from 'expres
import GitlabManager                                                from '../managers/GitlabManager';
import express                                                      from 'express';
import SharedExerciseHelper                                         from '../shared/helpers/Dojo/SharedExerciseHelper';
import logger                                                       from '../shared/logging/WinstonLogger';


declare type DojoMeta = Meta & {
    req: express.Request
};

declare type DojoCustomValidator = (input: any, meta: DojoMeta) => any;
declare type DojoCustomValidator = (input: unknown, meta: DojoMeta) => unknown;

declare type DojoCustomValidatorSchemaOptions = { errorMessage?: FieldMessageFactory | ErrorMessage, negated?: boolean, bail?: boolean | BailOptions, if?: CustomValidator | ValidationChain, options?: CustomValidator }

@@ -22,7 +23,7 @@ class DojoValidators {
        return arg as unknown as DojoCustomValidatorSchemaOptions;
    }

    private getParamValue(req: express.Request, path: string): any {
    private getParamValue(req: express.Request, path: string): unknown {
        return 'body' in req && path in req.body ? req.body[path] : req.query[path];
    }

@@ -30,7 +31,9 @@ class DojoValidators {
                                                               options: (value) => {
                                                                   try {
                                                                       return value == 'null' || value == 'undefined' || value == '' ? null : value;
                                                                   } catch ( e ) {
                                                                   } catch ( error ) {
                                                                       logger.error(`null sanitizer error: ${ error }`);

                                                                       return value;
                                                                   }
                                                               }
@@ -39,7 +42,7 @@ class DojoValidators {
    readonly jsonSanitizer = this.toValidatorSchemaOptions({
                                                               options: (value) => {
                                                                   try {
                                                                       return JSON.parse(value);
                                                                       return JSON.parse(value as string);
                                                                   } catch ( e ) {
                                                                       return value;
                                                                   }
@@ -54,7 +57,7 @@ class DojoValidators {
                                                                          path
                                                                      }) => {
                                                                          return new Promise((resolve, reject) => {
                                                                              const template = this.getParamValue(req, path);
                                                                              const template = this.getParamValue(req, path) as string;
                                                                              if ( template ) {
                                                                                  GitlabManager.checkTemplateAccess(template, req).then((templateAccess) => {
                                                                                      templateAccess !== StatusCodes.OK ? reject() : resolve(true);
@@ -77,10 +80,12 @@ class DojoValidators {
                                                                              } else {
                                                                                  return Config.assignment.default.template;
                                                                              }
                                                                          } catch ( e ) { }
                                                                          } catch ( error ) {
                                                                              logger.error(`Template url sanitizer error: ${ error }`);

                                                                              return value;
                                                                          }
                                                                      }
                                                                  });

    readonly exerciseResultsValidator = this.toValidatorSchemaOptions({
@@ -91,7 +96,7 @@ class DojoValidators {
                                                                              path
                                                                          }) => {
                                                                              return new Promise((resolve, reject) => {
                                                                                  const results = this.getParamValue(req, path);
                                                                                  const results = this.getParamValue(req, path) as string;
                                                                                  if ( results ) {
                                                                                      SharedExerciseHelper.validateResultFile(results, false).isValid ? resolve(true) : reject();
                                                                                  } else {
+2 −2
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ 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> {
    async repositoryCreationError(message: string, error: unknown, req: express.Request, res: express.Response, gitlabError: DojoStatusCode, internalError: DojoStatusCode, repositoryToRemove?: GitlabRepository): Promise<void> {
        logger.error(message);
        logger.error(error);

@@ -26,7 +26,7 @@ class GlobalHelper {
        }

        return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, `Unknown error: ${ message }`, internalError);
    };
    }
}


+14 −15
Original line number Diff line number Diff line
@@ -29,33 +29,32 @@ class GitlabManager {
                    DojoAuthorizationValue   : `Bearer ${ token }`
                }
            })).data;
        } catch ( e ) { }

        } catch ( e ) {
            return undefined;
        }
    }

    public async getUserById(id: number): Promise<GitlabUser | undefined> {
        try {
            const params: any = {};
            const user = (await axios.get<GitlabUser>(`${ this.getApiUrl(GitlabRoute.USERS_GET) }/${ String(id) }`, { params: params })).data;
            const user = (await axios.get<GitlabUser>(`${ this.getApiUrl(GitlabRoute.USERS_GET) }/${ String(id) }`)).data;

            return user.id === id ? user : undefined;
        } catch ( e ) { }

        } catch ( e ) {
            return undefined;
        }
    }

    public async getUserByUsername(username: string): Promise<GitlabUser | undefined> {
        try {
            const params: any = {};
            const params: Record<string, string> = {};
            params['search'] = username;
            const user = (await axios.get<Array<GitlabUser>>(this.getApiUrl(GitlabRoute.USERS_GET), { params: params })).data[0];

            return user.username === username ? user : undefined;
        } catch ( e ) { }

        } catch ( e ) {
            return undefined;
        }
    }

    async getRepository(idOrNamespace: string): Promise<GitlabRepository> {
        const response = await axios.get<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', encodeURIComponent(idOrNamespace)));
@@ -185,15 +184,15 @@ class GitlabManager {
    }

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

        let results: Array<GitlabTreeFile> = [];
        const results: Array<GitlabTreeFile> = [];

        while ( params !== undefined ) {
            const response = await axios.get<Array<GitlabTreeFile>>(address, {
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ class UserManager {
                                        }) as unknown as User ?? undefined;
    }

    async getUpdateFromGitlabProfile(gitlabProfile: GitlabProfile, refreshToken: string): Promise<User> {
    async getUpdateFromGitlabProfile(gitlabProfile: GitlabProfile): Promise<User> {
        await db.user.upsert({
                                 where : {
                                     id: gitlabProfile.id
+3 −3
Original line number Diff line number Diff line
@@ -5,16 +5,16 @@ import ExerciseManager from '../managers/ExerciseManager';
import AssignmentManager from '../managers/AssignmentManager';


type GetFunction = (id: string | number, ...args: Array<any>) => Promise<any>
type GetFunction = (id: string | number, ...args: Array<unknown>) => Promise<unknown>


class ParamsCallbackManager {
    protected listenParam(paramName: string, backend: Express, getFunction: GetFunction, args: Array<any>, indexName: string) {
    protected listenParam(paramName: string, backend: Express, getFunction: GetFunction, args: Array<unknown>, indexName: string) {
        backend.param(paramName, (req: express.Request, res: express.Response, next: express.NextFunction, id: string | number) => {
            getFunction(id, ...args).then(result => {
                if ( result ) {
                    this.initBoundParams(req);
                    (req.boundParams as any)[indexName] = result;
                    (req.boundParams as Record<string, unknown>)[indexName] = result;

                    next();
                } else {
+7 −0
Original line number Diff line number Diff line
@@ -58,18 +58,25 @@ class AssignmentRoutes implements RoutesManager {
        const assignment: Assignment | undefined = req.boundParams.assignment;

        if ( assignment && !assignment.published && !await AssignmentManager.isUserAllowedToAccessAssignment(assignment, req.session.profile) ) {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.gitlabId;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.gitlabLink;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.gitlabCreationInfo;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.gitlabLastInfo;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.gitlabLastInfoDate;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.staff;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            delete assignment.exercises;
        }
+4 −3
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ 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';
import { IFileDirStat }          from '../shared/helpers/recursiveFilesStats/RecursiveFilesStats';


class ExerciseRoutes implements RoutesManager {
@@ -187,8 +188,8 @@ class ExerciseRoutes implements RoutesManager {
        const repoTree: Array<GitlabTreeFile> = await GitlabManager.getRepositoryTree(req.boundParams.exercise!.assignment.gitlabId);

        let assignmentHjsonFile!: GitlabFile;
        let immutableFiles: Array<GitlabFile> = await Promise.all(Config.assignment.baseFiles.map(async (baseFile: string) => {
            let file = await GitlabManager.getFile(req.boundParams.exercise!.assignment.gitlabId, baseFile);
        const immutableFiles: Array<GitlabFile> = await Promise.all(Config.assignment.baseFiles.map(async (baseFile: string) => {
            const file = await GitlabManager.getFile(req.boundParams.exercise!.assignment.gitlabId, baseFile);

            if ( baseFile === Config.assignment.filename ) {
                assignmentHjsonFile = file;
@@ -220,7 +221,7 @@ class ExerciseRoutes implements RoutesManager {
    }

    private async createResult(req: express.Request, res: express.Response) {
        const params: { exitCode: number, commit: any, results: ExerciseResultsFile, files: any, archiveBase64: string } = req.body;
        const params: { exitCode: number, commit: Record<string, string>, results: ExerciseResultsFile, files: Array<IFileDirStat>, archiveBase64: string } = req.body;
        const exercise: Exercise = req.boundParams.exercise!;

        const result = await db.result.create({
+1 −1
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ class SessionRoutes implements RoutesManager {
            const gitlabUser = await GitlabManager.getUserProfile(params.accessToken);

            if ( gitlabUser ) {
                req.session.profile = await UserManager.getUpdateFromGitlabProfile(gitlabUser, params.refreshToken);
                req.session.profile = await UserManager.getUpdateFromGitlabProfile(gitlabUser);

                req.session.sendResponse(res, StatusCodes.OK);
                return;
Compare 4a5eb682 to 101cc268
Original line number Diff line number Diff line
Subproject commit 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989
Subproject commit 101cc26895eb0b5fe97e03bb96039e0cddd94391