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

Target

Select target project
  • Dojo_Project_Nguyen/backend/dojobackendapi
  • dojo_project/projects/backend/dojobackendapi
2 results
Select Git revision
Show changes
Commits on Source (22)
Showing
with 4289 additions and 1364 deletions
......@@ -4,6 +4,9 @@ workspace.xml
Wiki/.idea
ExpressAPI/src/config/Version.ts
redoc.html
OpenAPI.yaml-r
############################ MacOS
# General
......
......@@ -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
......
......@@ -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,8 +4,11 @@
#/--------------------------------------------------/
# development
DOTENV_VAULT_DEVELOPMENT="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"
DOTENV_VAULT_DEVELOPMENT="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"
# production
DOTENV_VAULT_PRODUCTION="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"
DOTENV_VAULT_PRODUCTION="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"
# test
DOTENV_VAULT_TEST="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"
dist
node_modules
logs
prisma
\ No newline at end of file
{
"root" : true,
"parser" : "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
]
}
\ No newline at end of file
Subproject commit 4d703a2dd39ec0c2b71bbbbda8900588c4e360bd
Subproject commit ffc5d65f9f0f0e825688177425e526131aa84631
<?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
<?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
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SwaggerSettings">
<option name="defaultPreviewType" value="SWAGGER_UI" />
</component>
</project>
\ No newline at end of file
openapi: 3.1.0
info:
title: Dojo API
version: 3.1.0
description: |
**Backend API of the Dojo project.**
See more information about the projet on
[Gitlab](https://githepia.hesge.ch/dojo_project/dojo).
license:
name: AGPLv3
identifier: AGPL-3.0-only
contact:
name: Michaël Minelli
email: dojo@minelli.me
servers:
- url: http://localhost:30993/
description: Development
- url: http://dojo-test.edu.hesge.ch/dojo/api/
description: Test (only from HES-GE network)
- url: https://rdps.hesge.ch/dojo/api/
description: Production
tags:
- name: General
description: ''
- name: Session
description: Routes that are used to manage the user's session
- name: Gitlab
description: Routes that are used to provide Gitlab informations
- name: Assignment
description: Routes that are used to manage assignments
- name: Exercise
description: Routes that are used to manage exercises
paths:
/health_check:
get:
tags:
- General
summary: Health check
description: This route can be used to check if the server is up and running.
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/DojoBackendResponse'
description: OK
default:
$ref: '#/components/responses/ERROR'
/login:
post:
tags:
- Session
summary: Login to Dojo app
description: |
This route can be used to connect the user to the backend and retrieve
informations about his access rights.
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
accessToken:
type: string
format: Gitlab access token
refreshToken:
type: string
format: Gitlab refresh token
required:
- accessToken
- refreshToken
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
nullable: true
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/refresh_tokens:
post:
tags:
- Session
summary: Refresh tokens
description: |
This route can be used to refresh the session. Gitlab tokens will be
refreshed and a new Dojo backend JWT token will be provided.
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
refreshToken:
type: string
format: Gitlab refresh token
required:
- refreshToken
responses:
'200':
description: The new Gitlab tokens as returned by Gitlab API.
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
properties:
access_token:
type: string
examples:
- de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54
token_type:
type: string
examples:
- bearer
expires_in:
type: number
examples:
- 7200
refresh_token:
type: string
examples:
- 8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1
scope:
type: array
examples:
- - api
- create_runner
- read_repository
- write_repository
items:
type: string
created_at:
type: number
examples:
- 1607635748
required:
- access_token
- token_type
- expires_in
- refresh_token
- scope
- created_at
default:
$ref: '#/components/responses/ERROR'
/test_session:
get:
tags:
- Session
summary: Test of the session
description: This route can be used to test the validity of the session token.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/DojoBackendResponse'
default:
$ref: '#/components/responses/ERROR'
/gitlab/project/{gitlabProjectIdOrNamespace}/checkTemplateAccess:
get:
tags:
- Gitlab
summary: Check access to template repository
description: |
This route can be used to check if the template repository is accessible by the Dojo user.
**🔒 Security needs:** any access rights
security:
- Clients_Token: [ ]
parameters:
- $ref: '#/components/parameters/gitlabProjectIdOrNamespace'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
nullable: true
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/assignments/{assignmentNameOrUrl}:
get:
tags:
- Assignment
summary: Get an assignment
description: |
This route can be used to get an assignment's informations by its name or its url.
If it's not published and the user is not a member of the staff (or admin), some informations will not be provided.
Informations that will be deleted:
- gitlabId
- gitlabLink
- gitlabCreationInfo
- gitlabLastInfo
- gitlabLastInfoDate
- staff
- exercises
**🔒 Security needs:** any access rights
security:
- Clients_Token: [ ]
parameters:
- $ref: '#/components/parameters/assignmentNameOrUrl'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/Assignment'
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/assignments:
post:
tags:
- Assignment
summary: Create a new assignment
description: |
**🔒 Security needs:** TeachingStaff or Admin roles
security:
- Clients_Token: [ ]
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
name:
type: string
description: Name of the assignment (must be unique)
members:
type: string
format: json
description: JSON string of an array of gitlab user objects
externalDocs:
description: Gitlab user object
url: https://docs.gitlab.com/ee/api/users.html#list-users
template:
type: string
format: url
description: URL of the template to use as base of the assignment
required:
- name
- members
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/Assignment'
'401':
$ref: '#/components/responses/UNAUTHORIZED'
default:
$ref: '#/components/responses/ERROR'
/assignments/{assignmentNameOrUrl}/publish:
patch:
tags:
- Assignment
summary: Publish an assignment
description: |
**🔒 Security needs:** TeachingStaff of the assignment or Admin role
security:
- Clients_Token: [ ]
parameters:
- $ref: '#/components/parameters/assignmentNameOrUrl'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
nullable: true
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/assignments/{assignmentNameOrUrl}/unpublish:
patch:
tags:
- Assignment
summary: Unpublish an assignment
description: |
**🔒 Security needs:** TeachingStaff of the assignment or Admin role
security:
- Clients_Token: [ ]
parameters:
- $ref: '#/components/parameters/assignmentNameOrUrl'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
nullable: true
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/assignments/{assignmentNameOrUrl}/exercises:
post:
tags:
- Exercise
summary: Create a new exercise
description: |
This route can be used to create a new exercise based on an assignment.
**🔒 Security needs:** The assignment must be published
security:
- Clients_Token: [ ]
parameters:
- $ref: '#/components/parameters/assignmentNameOrUrl'
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
members:
type: string
format: json
description: JSON string of an array of gitlab user objects
externalDocs:
description: Gitlab user object
url: https://docs.gitlab.com/ee/api/users.html#list-users
required:
- members
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/Exercise'
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
'419':
description: |
At least one of the members of the exercices have reached the maximum number of exercises for this assignment.
Users that have reached the maximum number of exercises are listed in the `data` field.
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
default:
$ref: '#/components/responses/ERROR'
/exercises/{exerciseId}/assignment:
get:
tags:
- Exercise
- Assignment
summary: Get the assignment of an exercise
security:
- ExerciseChecker_Secret: [ ]
parameters:
- $ref: '#/components/parameters/exerciseId'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
properties:
assignment:
$ref: '#/components/schemas/Assignment'
assignmentFile:
$ref: '#/components/schemas/AssignmentFile'
immutable:
type: array
items:
type: object
description: Gitlab file object
externalDocs:
description: Gitlab file object
url: https://docs.gitlab.com/ee/api/repository_files.html#get-file-from-repository
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
/exercises/{exerciseId}/results:
post:
tags:
- Exercise
summary: Get results of an exercise
security:
- ExerciseChecker_Secret: [ ]
parameters:
- $ref: '#/components/parameters/exerciseId'
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
exitCode:
type: number
description: The exit code of the exercise execution
commit:
type: string
format: json
description: JSON string of an array of gitlab user objects
externalDocs:
description: Gitlab commit object
url: https://docs.gitlab.com/ee/api/commits.html#get-a-single-commit
results:
type: string
format: json
description: JSON string of a DojoResult object (see in schemas)
externalDocs:
description: DojoResult object
url: '#/components/schemas/DojoResult'
files:
type: string
format: json
description: JSON string of an array of file objects
archiveBase64:
type: string
format: base64
description: Base64 encoded archive of the exercise's results
required:
- exitCode
- commit
- results
- files
- archiveBase64
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
nullable: true
'401':
$ref: '#/components/responses/UNAUTHORIZED'
'404':
$ref: '#/components/responses/NOT_FOUND'
default:
$ref: '#/components/responses/ERROR'
components:
securitySchemes:
Clients_Token:
type: http
scheme: bearer
bearerFormat: JWT
ExerciseChecker_Secret:
type: apiKey
in: header
name: ExerciseSecret
parameters:
gitlabProjectIdOrNamespace:
name: gitlabProjectIdOrNamespace
description: |
The id or the namespace of the project. The namespace is the
path of the project (e.g. `dojo_project/dojo`).
in: path
required: true
schema:
type: string
assignmentNameOrUrl:
name: assignmentNameOrUrl
description: The name or the url of an assignment.
in: path
required: true
schema:
type: string
exerciseId:
name: exerciseId
description: The id of an exercise.
in: path
required: true
schema:
type: string
format: uuidv4
schemas:
DojoBackendResponse:
type: object
properties:
timestamp:
type: string
examples:
- '1992-09-30T19:00:00.000Z'
code:
type: number
examples:
- 200
description:
type: string
examples:
- OK
sessionToken:
type: string
examples:
- JWT token (for content, see schema named 'SessionTokenJWT')
data:
type: object
properties: { }
required:
- timestamp
- code
- description
- sessionToken
- data
User:
type: object
properties:
id:
type: number
examples:
- 142
name:
type: string
examples:
- michael.minelli
mail:
type: string
examples:
- dojo@minelli.me
role:
type: string
enum:
- STUDENT
- TEACHING_STAFF
- ADMIN
gitlabUsername:
type: string
examples:
- michael.minelli
gitlabLastInfo:
type: object
properties: { }
externalDocs:
description: Gitlab user object
url: https://docs.gitlab.com/ee/api/users.html#list-users
isTeachingStaff:
type: boolean
examples:
- true
isAdmin:
type: boolean
examples:
- true
deleted:
type: boolean
examples:
- false
assignments:
type: array
items:
$ref: '#/components/schemas/Assignment'
exercises:
type: array
items:
$ref: '#/components/schemas/Exercise'
required:
- id
- role
- gitlabUsername
- isTeachingStaff
- isAdmin
- deleted
Assignment:
type: object
properties:
name:
type: string
examples:
- C_Hello_World
gitlabId:
type: number
examples:
- 30992
gitlabLink:
type: string
examples:
- https://githepia.hesge.ch/dojo_project
gitlabCreationInfo:
type: object
properties: { }
externalDocs:
description: Gitlab project object
url: https://docs.gitlab.com/ee/api/projects.html#get-single-project
gitlabLastInfo:
type: object
properties: { }
externalDocs:
description: Gitlab project object
url: https://docs.gitlab.com/ee/api/projects.html#get-single-project
gitlabLastInfoDate:
type: string
examples:
- '1992-09-30 19:00:00.000'
published:
type: boolean
examples:
- true
staff:
type: array
items:
$ref: '#/components/schemas/User'
exercises:
type: array
items:
$ref: '#/components/schemas/Exercise'
required:
- name
- gitlabId
- gitlabLink
- gitlabCreationInfo
- gitlabLastInfo
- gitlabLastInfoDate
- published
- staff
- exercises
Exercise:
type: object
properties:
id:
type: string
format: uuidv4
examples:
- eb5f2182-f5b1-42a9-80fc-cad384571053
assignmentName:
type: string
examples:
- C_Hello_World
name:
type: string
examples:
- DojoEx - C_Hello_World - michael.minelli
gitlabId:
type: number
examples:
- 93092
gitlabLink:
type: string
examples:
- https://githepia.hesge.ch/dojo_project/dojo
gitlabCreationInfo:
type: object
properties: { }
externalDocs:
description: Gitlab project object
url: https://docs.gitlab.com/ee/api/projects.html#get-single-project
gitlabLastInfo:
type: object
properties: { }
externalDocs:
description: Gitlab project object
url: https://docs.gitlab.com/ee/api/projects.html#get-single-project
gitlabLastInfoDate:
type: string
examples:
- '1992-09-30 19:00:00.000'
required:
- id
- assignmentName
- name
- gitlabId
- gitlabLink
- gitlabCreationInfo
- gitlabLastInfo
- gitlabLastInfoDate
DojoResult:
type: object
properties:
success:
type: boolean
examples:
- true
containerExitCode:
type: number
examples:
- 0
successfulTests:
type: number
examples:
- 3
failedTests:
type: number
examples:
- 0
successfulTestsList:
type: array
items:
type: string
examples:
- [ "Test 1", "Test 2", "Test 3" ]
failedTestsList:
type: array
items:
type: string
examples:
- [ ]
required:
- success
- containerExitCode
AssignmentFile:
type: object
properties:
dojoAssignmentVersion:
type: number
examples:
- 1
version:
type: number
examples:
- 1
immutable:
type: array
items:
type: object
properties:
description:
type: string
examples:
- Dockerfile of the unique container
path:
type: string
examples:
- Dockerfile
isDirectory:
type: boolean
examples:
- false
required:
- path
result:
type: object
properties:
container:
type: string
examples:
- hello_world
volume:
type: string
examples:
- hello_world_volume
required:
- container
required:
- dojoAssignmentVersion
- version
- immutable
- result
SessionTokenJWT:
type: object
properties:
profile:
$ref: '#/components/schemas/User'
iat:
type: string
examples:
- '1700749215'
required:
- profile
- iat
examples:
DojoBackendResponseERROR:
value:
timestamp: '1992-09-30T19:00:00.000Z'
code: 500
description: ERROR_MESSAGE
sessionToken: JWT token (for content, see schema named 'SessionTokenJWT')
data: null
responses:
UNAUTHORIZED:
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/DojoBackendResponse'
example:
timestamp: '1992-09-30T19:00:00.000Z'
code: 401
description: UNAUTHORIZED
sessionToken: JWT token (for content, see schema named 'SessionTokenJWT')
data: null
NOT_FOUND:
description: NOT FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/DojoBackendResponse'
example:
timestamp: '1992-09-30T19:00:00.000Z'
code: 404
description: NOT_FOUND
sessionToken: JWT token (for content, see schema named 'SessionTokenJWT')
data: null
ERROR:
description: Unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/DojoBackendResponse'
example:
timestamp: '1992-09-30T19:00:00.000Z'
code: 5XX or Internal code
description: This is an error message
sessionToken: JWT token (for content, see schema named 'SessionTokenJWT')
data: null
\ No newline at end of file
......@@ -9,5 +9,5 @@
"verbose": true,
"ext" : ".ts,.js",
"ignore" : [],
"exec" : "ts-node --files ./src/app.ts"
"exec" : "npm run lint; npm run build:openapi; ts-node --files ./src/app.ts"
}
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"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",
"build:openapi" : "sed -i -r \"1,20 s/^\\([ ]*version:\\).*$/\\1 $(jq -r .version package.json)/\" assets/OpenAPI/OpenAPI.yaml; npx @redocly/cli build-docs assets/OpenAPI/OpenAPI.yaml --output=assets/OpenAPI/redoc.html",
"build:project" : "npm run genversion; npx prisma generate && npx tsc --project ./ && cp -R assets dist/assets",
"build" : "npm run build:openapi; npm run build:project",
"database:migrate" : "npx prisma migrate deploy",
"database:seed" : "npm run genversion; npx prisma db seed",
"database:deploy" : "npm run database:migrate && npm run database:seed",
......@@ -21,9 +24,9 @@
"seed": "node dist/prisma/seed"
},
"dependencies" : {
"@prisma/client" : "^5.1.1",
"@prisma/client" : "^5.6.0",
"ajv" : "^8.12.0",
"axios" : "^1.4.0",
"axios" : "^1.6.2",
"compression" : "^1.7.4",
"cors" : "^2.8.5",
"dotenv" : "^16.3.1",
......@@ -31,39 +34,43 @@
"express" : "^4.18.2",
"express-validator" : "^7.0.1",
"form-data" : "^4.0.0",
"helmet" : "^7.0.0",
"http-status-codes": "^2.2.0",
"helmet" : "^7.1.0",
"http-status-codes" : "^2.3.0",
"json5" : "^2.2.3",
"jsonwebtoken" : "^9.0.0",
"knex" : "^2.4.2",
"jsonwebtoken" : "^9.0.2",
"morgan" : "^1.10.0",
"multer" : "^1.4.5-lts.1",
"mysql" : "^2.18.1",
"node" : "^20.5.0",
"node" : "^20.10.0",
"parse-link-header" : "^2.0.0",
"semver" : "^7.5.4",
"swagger-ui-express": "^5.0.0",
"tar-stream" : "^3.1.6",
"uuid" : "^9.0.0",
"winston" : "^3.8.2"
"uuid" : "^9.0.1",
"winston" : "^3.11.0"
},
"devDependencies": {
"@types/compression" : "^1.7.2",
"@types/cors" : "^2.8.13",
"@types/express" : "^4.17.17",
"@types/jsonwebtoken" : "^9.0.2",
"@types/morgan" : "^1.9.4",
"@types/multer" : "^1.4.7",
"@types/node" : "^20.4.7",
"@types/parse-link-header": "^2.0.1",
"@types/semver" : "^7.5.3",
"@types/tar-stream" : "^2.2.2",
"@types/uuid" : "^9.0.2",
"@redocly/cli" : "^1.5.0",
"@types/compression" : "^1.7.5",
"@types/cors" : "^2.8.17",
"@types/express" : "^4.17.21",
"@types/jsonwebtoken" : "^9.0.5",
"@types/morgan" : "^1.9.9",
"@types/multer" : "^1.4.11",
"@types/node" : "^20.10.3",
"@types/parse-link-header" : "^2.0.3",
"@types/semver" : "^7.5.6",
"@types/swagger-ui-express" : "^4.1.6",
"@types/tar-stream" : "^3.1.3",
"@types/uuid" : "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser" : "^6.13.2",
"dotenv-vault" : "^1.25.0",
"genversion" : "^3.1.1",
"nodemon" : "^3.0.1",
"prisma" : "^5.1.1",
"nodemon" : "^3.0.2",
"npm" : "^10.2.4",
"prisma" : "^5.6.0",
"ts-node" : "^10.9.1",
"typescript" : "^5.1.6",
"npm" : "^9.8.1"
"typescript" : "^5.3.2"
}
}
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 }));
}
}
......
......@@ -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,7 +13,9 @@ import logger from '../shared/logging/WinstonLogger';
import ParamsCallbackManager from '../middlewares/ParamsCallbackManager';
import ApiRoutesManager from '../routes/ApiRoutesManager';
import compression from 'compression';
import ClientVersionMiddleware from '../middlewares/ClientVersionMiddleware';
import ClientVersionCheckerMiddleware from '../middlewares/ClientVersionCheckerMiddleware';
import swaggerUi from 'swagger-ui-express';
import path from 'path';
class API implements WorkerTask {
......@@ -23,6 +25,17 @@ class API implements WorkerTask {
constructor() {
this.backend = express();
this.initSwagger();
this.initBaseMiddlewares();
this.backend.use(ClientVersionCheckerMiddleware.register());
ParamsCallbackManager.registerOnBackend(this.backend);
SessionMiddleware.registerOnBackend(this.backend);
ApiRoutesManager.registerOnBackend(this.backend);
}
private initBaseMiddlewares() {
this.backend.use(multer({
limits: { fieldSize: 100 * 1024 * 1024 }
}).none()); //Used for extract params from body with format "form-data", The none is for say that we do not wait a file in params
......@@ -30,14 +43,36 @@ class API implements WorkerTask {
this.backend.use(helmet()); //Help to secure express, https://helmetjs.github.io/
this.backend.use(cors()); //Allow CORS requests
this.backend.use(compression()); //Compress responses
}
this.backend.use(ClientVersionMiddleware.register());
ParamsCallbackManager.register(this.backend);
this.backend.use(SessionMiddleware.register());
private initSwagger() {
const options = {
swaggerOptions: {
url: '/docs/OpenAPI.yaml'
}
};
this.backend.get('/docs/OpenAPI.yaml', (req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/OpenAPI.yaml')));
this.backend.use('/docs/swagger', swaggerUi.serveFiles(undefined, options), swaggerUi.setup(undefined, options));
this.backend.get('/docs/redoc.html', (req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/redoc.html')));
ApiRoutesManager.registerOnBackend(this.backend);
this.backend.get('/docs/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<body>
<ul>
<li><a href="/docs/OpenAPI.yaml">OpenAPI</a></li>
<li>GUI
<ul>
<li><a href="/docs/swagger">Swagger</a></li>
<li><a href="/docs/redoc.html">Redoc</a></li>
</ul>
</li>
</ul>
</body>
</html>
`);
});
}
run() {
......
......@@ -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 {
......
......@@ -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);
};
}
}
......
......@@ -29,36 +29,35 @@ 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)));
async getRepository(projectIdOrNamespace: string): Promise<GitlabRepository> {
const response = await axios.get<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', encodeURIComponent(projectIdOrNamespace)));
return response.data;
}
......@@ -141,10 +140,10 @@ class GitlabManager {
return response.data;
}
async checkTemplateAccess(idOrNamespace: string, req: express.Request): Promise<StatusCodes> {
async checkTemplateAccess(projectIdOrNamespace: string, req: express.Request): Promise<StatusCodes> {
// Get the Gitlab project and check if it have public or internal visibility
try {
const project: GitlabRepository = await this.getRepository(idOrNamespace);
const project: GitlabRepository = await this.getRepository(projectIdOrNamespace);
if ( [ GitlabVisibility.PUBLIC.valueOf(), GitlabVisibility.INTERNAL.valueOf() ].includes(project.visibility) ) {
return StatusCodes.OK;
......@@ -154,7 +153,7 @@ class GitlabManager {
}
// Check if the user and dojo are members (with at least reporter access) of the project
const members = await this.getRepositoryMembers(idOrNamespace);
const members = await this.getRepositoryMembers(projectIdOrNamespace);
const isUsersAtLeastReporter = {
user: false,
dojo: false
......@@ -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, {
......