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
31 files
+ 4344
1390
Compare changes
  • Side-by-side
  • Inline

Files

+3 −0
Original line number Original line Diff line number Diff line
@@ -4,6 +4,9 @@ workspace.xml
Wiki/.idea
Wiki/.idea


ExpressAPI/src/config/Version.ts
ExpressAPI/src/config/Version.ts
redoc.html
OpenAPI.yaml-r



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




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




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

        - npm install
        - npm run lint


test:build:
test:build:
    stage: test
    stage: test
    image: node:latest
    image: node:latest
+11 −1
Original line number Original line Diff line number Diff line
@@ -17,7 +17,17 @@
- No modifications / Keep major and minors versions in sync with all parts of the project
- 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
### ✨ Feature
- Login to Dojo app via Gitlab OAuth
- Login to Dojo app via Gitlab OAuth
Original line number Original line Diff line number Diff line
@@ -4,8 +4,11 @@
#/--------------------------------------------------/
#/--------------------------------------------------/


# development
# development
DOTENV_VAULT_DEVELOPMENT="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"
DOTENV_VAULT_DEVELOPMENT="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"


# production
# production
DOTENV_VAULT_PRODUCTION="BbxoVGNL9qcv5se5ERis54TYJwS8QNJl7fsMpESxnbnq0xIqCeYg6SA8QZ/GsT90//438HSffBb5HNxIGEiFMdq7aKyHBo4N5Fr3PcWGLDL9Lj8CBq1J9SPz3GrWPOxzY7rF6qFaLJ2AsWDVtVcTZNlfmNkIVbEnDbjG6YF2fj0nCaZdq/czYHN6qplgGlZtRFC5C61fTFTfjzSlsP9vgMhdZxEy7dsqFGx26ZtKlo4SQLHO1t3AFcGbWp4VYsLAAXR5FO765nQCZHDs3xRemQnepjDiByQtNX188U6F9LOMWOtRku60HJ/tFCPpWzMGh9bpagyJc+79YHC52P9P7Fm4oWFvoO17rHTE+0uQTii+dy0IAsP2xDX7mMJpnc34zqwesRT6X8zx7Qvd/rdx3bbT+Ej5aNSj0f0nmd2Np7A+1tpYkPQZ9X3L4UQupSCNku9PEZISBl6RiUXJ1IBD1C+z/cQ8D9Jp4Rcp63Omk/i/90BUMV0yg3yww9cjhi3rO1TYLDUNvxVbrmrqtIN5Z8/34ekHA6BQqw6Rxh/e5XOeOf7otWY9TmVxy6/KebjELMI3o8MRb48XnBhelZNY3o4DprrY/OnffWEsxWVw79n0/TlWBz4jPwSBWhXeerKJmKfIiMfdRkyIZ6bAAH3qe1TuEAZ+u0W9D7sxBnGz92iDiBSAe8sOBZz3Thna/0iCbhtdv+MRQzgi8P0sgISqr6dxKRBntghae7HA+mtwf8cSvKvJJIoh4glUPslhcuFQCenB/b9OIiAfseIKr/whr3Eymzf+0ZWv/WMDzYYO2q/sZN3bd9WRv/RZdQCssW6EY+Cw5p/BPZDJb3fRNDMuZahAK6B/XaR0Nb5Fgce4mdHvPcKfpOpL24bcWfcKNniNdjOYFjdKz65HZkbWyZ0uVJ+0n6noSuc0Op5RHpJ0vWVc4vaO+N2rPMiV3RZd0Itg7sz19E1LZBiByny2uTXWTuyhcVwipeHszMQRsD1/m+J6PTMoovrreGo97h1npEL8yD8qUyrModARqn9FfYwD8ENM9xTrU8xXDUTAoMNUiZjO24J0XpUJa0r6Ycofv39YtSNwY9uZKbLp9cIE3noMLlF9WAEqXI4rAeuqQWBWW/vmf/I+WpOzGBUzQ4KltH6yhdt1lxQpUBZmGGwQ3VT9hhu2miZf5iX5nUJ07HhMFcyHWozh4QuuBs4u3de3g4ExybGjaNFKCqqsvAVaJtMKjTuL6+gzm+U7hmKFSo5NOzoP35U/J1h3tt2PJL3JIaKKLF2zvtrFBPt311h0LhVqDNEwIuvjVEjMFj7RYm/7gm+b40bQaRrfHBC8nIOapUkNmJm96qUqaRrBkRhJAEBWp8itX92gg5fljVb/30Bv5qPS8Hrc/h89pn22l1hBo+SStUAA/dXa9CaGQxMIfxqATkWtEOgm1PJ1BgvFWH9cCpd4RMtbq4RTM52482TtEV0HTfXESQQ7qJyAKE8ZMpKbCKU103THzTg+YckV7eOKDQAYp8J+T5uNxvo8EZ+1g7KpDL2YZltEWdwCC5TN+sC+N5b3C5WQr8k72fzVCUOApZpDCYr7SIl8cH/eEIogcE1LHUctm/frMgEd3w7QjYln2AVB4GCdSgmGIYP+V1IKkS4XulBrbB6NDmHTglR5ZdeeTK8QdKSfyD0PXps5kQWO9gy+vdroDq957a/3qRxigW8wEryy+RhUN3oDtV5X6/cM4OLgsaXVmdUUTKRKiO1Rqqn4SYJLndqRf6N/0Ac13P7C4hVrpscLnLgG773N0I7XsHZmxkqsOM7TU3utk7Ys4Fcu4sEPYXJwZgzhHngTHhvoZzPqoFRWRg3AjTEqIBwCKZwTR3KhKwaf/jqO0YDu8SaMBqJ6vZl5dMk8BvMROUpB6mwy8++2Bzk6HSqNnfUvBp7lC/tPVjrPvsT7HzcRlEMsB16pgOvonGlbldN+yNzxMvEggQpxgWwNxrciGZyEe04Atkoli5YUCi7C984bWLpaH65e4191AGhT9QwTnd6ojqwPmhuIebgmXHfYM75KAqhvUIk+rkUNNgNI8xwdgF7tZiYEQcCnHf+D6/Cs9yppOjKf0Uff49ikZclTtj3JbW1aUBK/JS/0UeCZYY4ezH7a1Lpcf1tTrc15lvaU//bq/VsR2H6cRoqrQeTahe7ztkO5EX2Wnznxd+HWrTzP1aj8H1zxvqwsLTol0CZv/hC3sk984DbALkDhbze7/dfnSYcFVL9Rf/UePBURl/RtV94PCC7PD/umocOe0psFlKbqJAn6q2QULk6T/F/nObRc5WLkEbYdHQdpszuIsY7ComnJKb5yfR4Yt7DC5uaXD2EG4ctJh4UsyecXJkG+pT0gFISoXhUk3A/sIUJxlfNYKEkIuEsr+uea8fNu0JZ7huCRyzB4457mlnNGxa03Jp5d4zA1qCs2hoNKBiV/AfvuoJMcSKgM/6OLSj9FgweEh9PepZ/Rlt16kL7uYaob9u8vhGQ0mzxDv8l4DxV9OoRWXG1mu+BLSYQ0Er9gmBD7Z2EbsNxKB4GD9EBySr/Gt9AFIEYVQuzK4ZcmyVc536PkYEdj1uGt0drvmm1S8a4R8QGOTBI+AOehH7244s7cB25t3/pF5R49FL0JrtpRYAnJabCrZGtbaXDbpm4pX4J/bcYuuEC50mF73F6iMRahQfvQOb3S1zXPhw7RWvyXNPeREafR9IMa7GEdTR3sGpTqiA2Do2+cYXGOPjHNK/wnjTozdB0pAwvOn7n6od0n+ElMMGc40nrHK6JEiE5wa3WvQANHrhdPjgNDDNKF2ZbAyh31svBihaoPhUfTLXk9T6flEG299Ep4MvFppiKuIpt5QQ9s33kBRtMBECe6UfIfbZwU95IWq3yMVEGo085xkm3eyQXEelDatJwR45X/qb2MYZ7SAuyZC5bADvd//nsc3hETqMX9DQKFfq3Ec7ZwMnJkOtLo4/pVi+Qr6tlHxHcvU6t8NgMC+BeM4Z1Z3IzKH2IVve7b48LA0HP7C2z/v0Xc2oi8FEulhdcwa1d46SbMSSTNgAFzkZooqksdbz817Sd/atFi+clhDNxkw9MZGyVP8z4VuO8BAVWC6hINccV2N5NBbErYFHYXZBmIdI9bQU1Cq8YAPw1l2bRcSh1mFkp1/XhA0/9/9MbgJAliktLy4BTMLdD8dIljwQ6gDwKIPONhOS86fW0/ub2hOdGncw40rn43Nvxi5mACuacYjc42/dspMnSXREavwxz0xP31g83KACmjovqvwGbM8QlegBcAx/pUGmUDiNHxxXQDJ4LZoshJ7wr1dkVocF6blyf0wEdxnM3RyKiVIJh5463YQx5DbzYDD70ZPBRJGLCbK4b0Dlz90bRthdSQVYQnsAatfaFnhGEvIv/2UptE9uTF3eLuQTmTsHljRSwXjg=="
DOTENV_VAULT_PRODUCTION="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"

# test
DOTENV_VAULT_TEST="rVF2z29+LGw9TGbiqm9SnQCrtNLLs7ufpz+4r05KP6fcZ6xHMdkWizrPUs1ay8c4IFGKc0E7gVNTpaamE87bXAJdq/rraIIzCl9D9jlXy/RdO8S/YzjIxJzYc7yNSzPed8QzMsoDT+a4OTGB36T8EWST8sVGyBG6K7oIh+OZ1cRka/oDz0Freio1BKGk3P/rX2uLW0TrT9BcH8+lbAr0rrgxgdoqBdrVp2qBnMM4VgEbOZe0SC4x2viW6p4n51RIG+0vAhxc5FK1S55+ZV+4mzRlMUYuv8NmK/+Dc6oDnZoup8Lchbk9ProH4P60vTbuPDUmRSgwyr5BCjJ8wt7WxVgqAgLEKaWVmDkrR9BxWG0tozbeD4Ed/mahpdQMDtciZeK460qdSHQ6MmZEKzR4ELyv864NMZNLM3RI+iZy8S8xycLYlKe82Ts+o8jceJQaN3466y1P5IA8pV6x020DXNfzIsutCyQEZJys9F79tz+xIBdZ2dan0TdvwZSh4TmhG/b60SFDnKUGJ4rc0LiYVqsTsJSrW9VBGhfpo9WOqjZQXabRxDHSSd8SeP9n+hHsOgEirV3pHYgS4Wmf5aTkQVimvnSxvG+/CZDwD3/7K3UzTI26rBP+mtMOoZ+pEquhp58wcekyiOhQth+dshJBag+WdhGMS6W7YCtZvA/xUH5kpZht9pe+AFxicANHD1bQS+vfscBSI7aH7/h4+9e3vRjNgxmVJYlVu6Njo4QppDmfpgN29HIGGpvZABfEqg7FF4Hg89vdcOnwSFNPhiEW99H1ULAYTRzYfPD6t7HP8E1tLS4gGOAPXp2uP5CvqL3o9pyy1yivhmDdgsS86XH7/QDoBRn6z0emkGBEiWo0PBkkgBgheaHQf7VLGZ9L4R8fcjwreF5su7DDgFDOGGqyBciGslYadKszDdFUklqVeK2Hb6UghN5Brx+arFge6N0vAupi7CXb+Qj3US4NsjmQdimW0lfcCLz3x6gk6axtVeIAi1yGJFJwCp34UUFnPL+p9jD5vgnFVMlbDfQIQ7K1EQlvER0aYNY9Oy65dkKPeI4N5c2Nk+KTr5zipwQ4h7wUH6rCW2sM6InWhqMirqpYY0GCYljM82G6z08Q8f+D0mdI0fCwrWKWvyfLfx+BOqPLJTpzR9xAZw3mBwJe8HztnnpnySZ8zMgxlOrxv36K8B/0X6B3kq0mFSlBQAm68lZvmqB+EBAkEMYgGA/9voZ0/AJVxebB7ePhOh7aTz24PqZhEF+CuS/EhkKJdVMYKbeqgXClgT6NhVIYBiez6kimajzrH8hPAIEdoMrE373uw48WEzPtBjK/l+TKNzCwihnCo3+zb7h1pDzMhl6NsaRXCqy/J231YzkfRN1zprsHGUexNhIXL6OY/mbWdo/FdG8BKurYiWTgeJvxU7sV5oCQHgdvr7wbLqmetvmUxn3XuIw/VqqI//R+Mn3GYBfmreGPvuhsNBH59Ua6Elv2XGcwW5QMDQSUeLb7FkrMmKgSBVWlfbfcwrNXY1TDa83tmz/iyT47FQ4OGjwSHAbQ01rRxRUMaw11jnhVl6dGVvT+W54FKXlqAEkzzdDZMem9G0yxvLlZc9+oChZZHECep+P+FkEQx2Ow64bs0/4jHdx2MH3xnbRJP1drGnnDFVzcltE0Ez7q5zMWgdgwzyh/mkWMFs3tv6KrQQsyrAD/1ZeHwnx+ytfhUXXSgG/LTXVW/IRbab1unOwtywito2YqPtLeqBhvkMfXLIWXDPvN76V1FrHcemKuJrtrqE42BlCWxIt6gMhSlS4R2dd3gDR61/PddpUj66roD4DAFKBJPHLfiyWoRc8Nwo5hT4SEOT7HgAqGj0Vx0A6Y8cqdRGn7DG1YPULSjO+LXIbaWfRdWQy/KYQZ/5p5nkznpewymzPE1ssB5MC7aGOGgMcTk5rOCugp4AVPg3vncwH71ugT3MoSVed1f6bIB3DHUZwpXgC0q3wUkqSFUgpTQMpEWMoPQmYa6THaI4jQzM0I1gLZgl+3HKIqUii6mizP33uWWlmk549IFnARb9AHlCDLpNt0+j3FzxmpNbmbgaTM4sGfX9NzwipRJWMN+RveSnkp3WVT+1CnEQuymFtF3UlDqK5opqDMFUi/UUQfgL/erMEZgjpRMuPhxriIBOQj2gzllMbwilSiX2wXvQWd2URMX/W41EF5gZDkb7nvrS7q8w72hrfb4B+R/oYA+j4V+2ZVXBVQC/fLGiGhMCjJ2Wq9rZe1YUz3Q9uCiLcaUjKlFO7vj94kjT+/DPpNHhfynofV04D6AI3NkZrbKIqrCzgrR7Rd3gL5x6aVVEXCV0jjx/OROc1NXmMqdsmuV57bvyw3GPscSwfZoZf0mrfq8Cqb2gWkukbFfl7JDGvWX/fZFqbsfcIA/+vVc4nGSR2k6bu0/fT98WULCDKKsLy4CEMawOmdKYOYoVG5Fr8VHnnlo/9rckKm1iAB1kzVLv5vZblFy6kj9/e/b1ypA7bS+prlvd9Ni/1W2/mACpLphFR9Qeay6xIeBUk3feEeK16v4pjU9KMFaaRhPqfS6waaqMrUbRtthoHDQY/S3AI0ywR/DXgUVXIOLJiBy6eJdtnJJUOLQkGB+VQFnfdj4sB4FvbamYv9XFN97ldKie0/iPkrzm3o0+XX43ENZp8sdgLX/efUgu1RzQ378RyXq3ljQTWZ5FrFfTQ8N0/8BV4GJjaIaNCVUWxTSAOAnvmWxoU8dsX1Ya8TUrlx6TVTbVAWCHTThLJMCM3IYzXYKkgFfCj7i1UtNj6hBUDROQ8hz4QfSmt5Fk+je5Jk4iQtk+M4NEkd2UZOnSVd5GDO5vENAE7vdcXIKSer9zC4gp3mW05AYZWEnxrQ4iqyYvCzmkFHD/w+AYwXMbEY2XL4DWtzZU0F+gEpAI8dzcTEb871Bj40o79Eicol2nYn9u1m+ZtoJKWYLY1NdcYXpPcfBddJUBGj19GKOWvLAsVFCmHgNABkHb6GNkkJ2sYg3eVWM2ltmVpKPLME/rT/d+0WYArWnlSzncqDVke/xb8f646w4ntc+KkGEWiiZE2OkDw9dOOsze8z9Oy8jN4dj7R2pYg5BuMBl8SyYnoeHdyK49qXjgPBr77goF0ns3j2DgErgYU7tJwfSL3PoX9n1MdZwjB2O3izj1sfFeDRmoKxie5trZOAUUjNOhCyVQNQnpuXl4tAAtKxlknxBSWDFHJNW5GPUxCTbVl8BfjmEy98/XwG9b9//yqnP9po/bKMVismdliiONjVFc8vBk2qnO1dK0OGTxoDdBP8gQTO4R2L3ByU6IaVpN7+VHUOXYxJBf+uCClefNtTGa3yB02bGSTjVM5P8KChl1FIWRSkuAfJXe0OfQUClhcXiShRSaqBN1qfy7/r7xKIVZKxvHONWS4uZcS/N6phcPOzRAZb0pNPiM6GQ7yCWKybYf7vyx4JynEOx3b3hh42S/m4XkjO3o0NqeKlCvO/mIcOGiAj+BUyZ0jYy9l/W2GaSMc7VQ7vDKrKmQQF+t67HJ+IeAii/ksJ5CtPRgW0AVc="
+4 −0
Original line number Original line Diff line number Diff line
dist
node_modules
logs
prisma
 No newline at end of file
+11 −0
Original line number Original line 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
Original line number Original line 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 Original line 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
+6 −0
Original line number Original line Diff line number Diff line
<?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
Original line number Original line Diff line number Diff line
@@ -9,5 +9,5 @@
    "verbose": true,
    "verbose": true,
    "ext"    : ".ts,.js",
    "ext"    : ".ts,.js",
    "ignore" : [],
    "ignore" : [],
    "exec"   : "ts-node --files ./src/app.ts"
    "exec"   : "npm run lint; npm run build:openapi; ts-node --files ./src/app.ts"
}
}
Original line number Original line Diff line number Diff line
{
{
    "name"           : "dojo_backend_api",
    "name"           : "dojo_backend_api",
    "description"    : "Backend API of the Dojo project",
    "description"    : "Backend API of the Dojo project",
    "version"        : "3.0.1",
    "version"        : "3.1.0",
    "license"        : "AGPLv3",
    "license"        : "AGPLv3",
    "author"         : "Michaël Minelli <dojo@minelli.me>",
    "author"         : "Michaël Minelli <dojo@minelli.me>",
    "main"           : "dist/src/app.js",
    "main"           : "dist/src/app.js",
    "scripts"        : {
    "scripts"        : {
        "clean"             : "rm -R dist/*",
        "clean"             : "rm -R dist/*",
        "dotenv:build"      : "npx dotenv-vault local build",
        "dotenv:build"      : "npx dotenv-vault local build",
        "lint"              : "npx eslint .",
        "genversion"        : "npx genversion -s -e src/config/Version.ts",
        "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:migrate"  : "npx prisma migrate deploy",
        "database:seed"     : "npm run genversion; npx prisma db seed",
        "database:seed"     : "npm run genversion; npx prisma db seed",
        "database:deploy"   : "npm run database:migrate && npm run database:seed",
        "database:deploy"   : "npm run database:migrate && npm run database:seed",
@@ -21,9 +24,9 @@
        "seed": "node dist/prisma/seed"
        "seed": "node dist/prisma/seed"
    },
    },
    "dependencies"   : {
    "dependencies"   : {
        "@prisma/client"   : "^5.1.1",
        "@prisma/client"    : "^5.6.0",
        "ajv"               : "^8.12.0",
        "ajv"               : "^8.12.0",
        "axios"            : "^1.4.0",
        "axios"             : "^1.6.2",
        "compression"       : "^1.7.4",
        "compression"       : "^1.7.4",
        "cors"              : "^2.8.5",
        "cors"              : "^2.8.5",
        "dotenv"            : "^16.3.1",
        "dotenv"            : "^16.3.1",
@@ -31,39 +34,43 @@
        "express"           : "^4.18.2",
        "express"           : "^4.18.2",
        "express-validator" : "^7.0.1",
        "express-validator" : "^7.0.1",
        "form-data"         : "^4.0.0",
        "form-data"         : "^4.0.0",
        "helmet"           : "^7.0.0",
        "helmet"            : "^7.1.0",
        "http-status-codes": "^2.2.0",
        "http-status-codes" : "^2.3.0",
        "json5"             : "^2.2.3",
        "json5"             : "^2.2.3",
        "jsonwebtoken"     : "^9.0.0",
        "jsonwebtoken"      : "^9.0.2",
        "knex"             : "^2.4.2",
        "morgan"            : "^1.10.0",
        "morgan"            : "^1.10.0",
        "multer"            : "^1.4.5-lts.1",
        "multer"            : "^1.4.5-lts.1",
        "mysql"             : "^2.18.1",
        "mysql"             : "^2.18.1",
        "node"             : "^20.5.0",
        "node"              : "^20.10.0",
        "parse-link-header" : "^2.0.0",
        "parse-link-header" : "^2.0.0",
        "semver"            : "^7.5.4",
        "semver"            : "^7.5.4",
        "swagger-ui-express": "^5.0.0",
        "tar-stream"        : "^3.1.6",
        "tar-stream"        : "^3.1.6",
        "uuid"             : "^9.0.0",
        "uuid"              : "^9.0.1",
        "winston"          : "^3.8.2"
        "winston"           : "^3.11.0"
    },
    },
    "devDependencies": {
    "devDependencies": {
        "@types/compression"      : "^1.7.2",
        "@redocly/cli"                    : "^1.5.0",
        "@types/cors"             : "^2.8.13",
        "@types/compression"              : "^1.7.5",
        "@types/express"          : "^4.17.17",
        "@types/cors"                     : "^2.8.17",
        "@types/jsonwebtoken"     : "^9.0.2",
        "@types/express"                  : "^4.17.21",
        "@types/morgan"           : "^1.9.4",
        "@types/jsonwebtoken"             : "^9.0.5",
        "@types/multer"           : "^1.4.7",
        "@types/morgan"                   : "^1.9.9",
        "@types/node"             : "^20.4.7",
        "@types/multer"                   : "^1.4.11",
        "@types/parse-link-header": "^2.0.1",
        "@types/node"                     : "^20.10.3",
        "@types/semver"           : "^7.5.3",
        "@types/parse-link-header"        : "^2.0.3",
        "@types/tar-stream"       : "^2.2.2",
        "@types/semver"                   : "^7.5.6",
        "@types/uuid"             : "^9.0.2",
        "@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",
        "dotenv-vault"                    : "^1.25.0",
        "genversion"                      : "^3.1.1",
        "genversion"                      : "^3.1.1",
        "nodemon"                 : "^3.0.1",
        "nodemon"                         : "^3.0.2",
        "prisma"                  : "^5.1.1",
        "npm"                             : "^10.2.4",
        "prisma"                          : "^5.6.0",
        "ts-node"                         : "^10.9.1",
        "ts-node"                         : "^10.9.1",
        "typescript"              : "^5.1.6",
        "typescript"                      : "^5.3.2"
        "npm"                     : "^9.8.1"
    }
    }
}
}
Original line number Original line Diff line number Diff line
import path    from 'node:path';
import path    from 'node:path';
import cluster from 'node:cluster';
import cluster from 'node:cluster';
import myEnv = require('dotenv');
import dotenvExpand = require('dotenv-expand');




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


Original line number Original line 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 } : {});
        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 {
        try {
            reasonPhrase = getReasonPhrase(code);
            reasonPhrase = getReasonPhrase(code);
        } catch {}
        } catch { /* empty */ }


        return {
        return {
            timestamp   : (new Date()).toISOString(),
            timestamp   : (new Date()).toISOString(),
@@ -67,8 +67,8 @@ class Session {
     Send a response to the client
     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
     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) {
    sendResponse(res: express.Response, code: number, data?: unknown, descriptionOverride?: string, internalCode?: number) {
        Promise.resolve(data).then((toReturn: any) => {
        Promise.resolve(data).then((toReturn: unknown) => {
            this.getResponse(internalCode ?? code, toReturn, descriptionOverride).then(response => {
            this.getResponse(internalCode ?? code, toReturn, descriptionOverride).then(response => {
                res.status(code).json(response);
                res.status(code).json(response);
            });
            });
Original line number Original line Diff line number Diff line
@@ -13,7 +13,9 @@ 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';
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 {
class API implements WorkerTask {
@@ -23,6 +25,17 @@ class API implements WorkerTask {
    constructor() {
    constructor() {
        this.backend = express();
        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({
        this.backend.use(multer({
                                    limits: { fieldSize: 100 * 1024 * 1024 }
                                    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
                                }).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(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
        this.backend.use(compression()); //Compress responses
    }


        this.backend.use(ClientVersionMiddleware.register());
    private initSwagger() {

        const options = {
        ParamsCallbackManager.register(this.backend);
            swaggerOptions: {

                url: '/docs/OpenAPI.yaml'
        this.backend.use(SessionMiddleware.register());
            }
        };
        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() {
    run() {
Original line number Original line Diff line number Diff line
@@ -5,13 +5,14 @@ import { BailOptions, ValidationChain } from 'expres
import GitlabManager                                                from '../managers/GitlabManager';
import GitlabManager                                                from '../managers/GitlabManager';
import express                                                      from 'express';
import express                                                      from 'express';
import SharedExerciseHelper                                         from '../shared/helpers/Dojo/SharedExerciseHelper';
import SharedExerciseHelper                                         from '../shared/helpers/Dojo/SharedExerciseHelper';
import logger                                                       from '../shared/logging/WinstonLogger';




declare type DojoMeta = Meta & {
declare type DojoMeta = Meta & {
    req: express.Request
    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 }
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;
        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];
        return 'body' in req && path in req.body ? req.body[path] : req.query[path];
    }
    }


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

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


                                                                              return value;
                                                                              return value;
                                                                          }
                                                                          }
                                                                      }
                                                                  });
                                                                  });


    readonly exerciseResultsValidator = this.toValidatorSchemaOptions({
    readonly exerciseResultsValidator = this.toValidatorSchemaOptions({
@@ -91,7 +96,7 @@ class DojoValidators {
                                                                              path
                                                                              path
                                                                          }) => {
                                                                          }) => {
                                                                              return new Promise((resolve, reject) => {
                                                                              return new Promise((resolve, reject) => {
                                                                                  const results = this.getParamValue(req, path);
                                                                                  const results = this.getParamValue(req, path) as string;
                                                                                  if ( results ) {
                                                                                  if ( results ) {
                                                                                      SharedExerciseHelper.validateResultFile(results, false).isValid ? resolve(true) : reject();
                                                                                      SharedExerciseHelper.validateResultFile(results, false).isValid ? resolve(true) : reject();
                                                                                  } else {
                                                                                  } else {
Original line number Original line Diff line number Diff line
@@ -8,7 +8,7 @@ import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode';




class GlobalHelper {
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(message);
        logger.error(error);
        logger.error(error);


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


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




Original line number Original line Diff line number Diff line
@@ -29,36 +29,35 @@ class GitlabManager {
                    DojoAuthorizationValue   : `Bearer ${ token }`
                    DojoAuthorizationValue   : `Bearer ${ token }`
                }
                }
            })).data;
            })).data;
        } catch ( e ) { }
        } catch ( e ) {

            return undefined;
            return undefined;
        }
        }
    }


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


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

            return undefined;
            return undefined;
        }
        }
    }


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


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

            return undefined;
            return undefined;
        }
        }
    }


    async getRepository(idOrNamespace: string): Promise<GitlabRepository> {
    async getRepository(projectIdOrNamespace: string): Promise<GitlabRepository> {
        const response = await axios.get<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', encodeURIComponent(idOrNamespace)));
        const response = await axios.get<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', encodeURIComponent(projectIdOrNamespace)));


        return response.data;
        return response.data;
    }
    }
@@ -141,10 +140,10 @@ class GitlabManager {
        return response.data;
        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
        // Get the Gitlab project and check if it have public or internal visibility
        try {
        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) ) {
            if ( [ GitlabVisibility.PUBLIC.valueOf(), GitlabVisibility.INTERNAL.valueOf() ].includes(project.visibility) ) {
                return StatusCodes.OK;
                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
        // 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 = {
        const isUsersAtLeastReporter = {
            user: false,
            user: false,
            dojo: false
            dojo: false
@@ -185,15 +184,15 @@ class GitlabManager {
    }
    }


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


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


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


    async getUpdateFromGitlabProfile(gitlabProfile: GitlabProfile, refreshToken: string): Promise<User> {
    async getUpdateFromGitlabProfile(gitlabProfile: GitlabProfile): Promise<User> {
        await db.user.upsert({
        await db.user.upsert({
                                 where : {
                                 where : {
                                     id: gitlabProfile.id
                                     id: gitlabProfile.id
Original line number Original line Diff line number Diff line
@@ -6,7 +6,7 @@ import { HttpStatusCode } from 'axios';
import DojoStatusCode     from '../shared/types/Dojo/DojoStatusCode';
import DojoStatusCode     from '../shared/types/Dojo/DojoStatusCode';




class ClientVersionMiddleware {
class ClientVersionCheckerMiddleware {
    register(): (req: express.Request, res: express.Response, next: express.NextFunction) => void {
    register(): (req: express.Request, res: express.Response, next: express.NextFunction) => void {
        return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
        return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
            if ( req.headers['client'] && req.headers['client-version'] ) {
            if ( req.headers['client'] && req.headers['client-version'] ) {
@@ -32,4 +32,4 @@ class ClientVersionMiddleware {
}
}




export default new ClientVersionMiddleware();
export default new ClientVersionCheckerMiddleware();
 No newline at end of file
 No newline at end of file
Original line number Original line Diff line number Diff line
@@ -5,16 +5,16 @@ import ExerciseManager from '../managers/ExerciseManager';
import AssignmentManager from '../managers/AssignmentManager';
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 {
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) => {
        backend.param(paramName, (req: express.Request, res: express.Response, next: express.NextFunction, id: string | number) => {
            getFunction(id, ...args).then(result => {
            getFunction(id, ...args).then(result => {
                if ( result ) {
                if ( result ) {
                    this.initBoundParams(req);
                    this.initBoundParams(req);
                    (req.boundParams as any)[indexName] = result;
                    (req.boundParams as Record<string, unknown>)[indexName] = result;


                    next();
                    next();
                } else {
                } else {
@@ -33,7 +33,7 @@ class ParamsCallbackManager {
        }
        }
    }
    }


    register(backend: Express) {
    registerOnBackend(backend: Express) {
        this.listenParam('assignmentNameOrUrl', backend, (AssignmentManager.get as GetFunction).bind(AssignmentManager), [ {
        this.listenParam('assignmentNameOrUrl', backend, (AssignmentManager.get as GetFunction).bind(AssignmentManager), [ {
            exercises: true,
            exercises: true,
            staff    : true
            staff    : true
Original line number Original line Diff line number Diff line
@@ -31,7 +31,7 @@ class SecurityMiddleware {
                                isAllowed = isAllowed || (req.boundParams.assignment?.published ?? false);
                                isAllowed = isAllowed || (req.boundParams.assignment?.published ?? false);
                                break;
                                break;
                            case SecurityCheckType.EXERCISE_SECRET:
                            case SecurityCheckType.EXERCISE_SECRET:
                                isAllowed = isAllowed || req.headers.authorization?.replace('ExerciseSecret ', '') === req.boundParams.exercise!.secret;
                                isAllowed = isAllowed || (req.headers.ExerciseSecret as string | undefined) === req.boundParams.exercise!.secret;
                                break;
                                break;
                            default:
                            default:
                                break;
                                break;
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 { Express } from 'express-serve-static-core';




class SessionMiddleware {
class SessionMiddleware {
    register(): (req: express.Request, res: express.Response, next: express.NextFunction) => void {
    registerOnBackend(backend: Express) {
        return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
        backend.use(async (req: express.Request, res: express.Response, next: express.NextFunction) => {
            req.session = new Session();
            req.session = new Session();
            await req.session.initSession(req, res);
            await req.session.initSession(req, res);


            return next();
            return next();
        };
        });
    }
    }
}
}


Original line number Original line Diff line number Diff line
@@ -49,8 +49,8 @@ class AssignmentRoutes implements RoutesManager {
        backend.get('/assignments/:assignmentNameOrUrl', SecurityMiddleware.check(true), this.getAssignment);
        backend.get('/assignments/:assignmentNameOrUrl', SecurityMiddleware.check(true), this.getAssignment);
        backend.post('/assignments', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.assignmentValidator), this.createAssignment);
        backend.post('/assignments', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.assignmentValidator), this.createAssignment);


        backend.patch('/assignments/:assignmentNameOrUrl/publish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.changeAssignmentPublishedStatus(true));
        backend.patch('/assignments/:assignmentNameOrUrl/publish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.publishAssignment);
        backend.patch('/assignments/:assignmentNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.changeAssignmentPublishedStatus(false));
        backend.patch('/assignments/:assignmentNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.unpublishAssignment);
    }
    }


    // Get an assignment by its name or gitlab url
    // Get an assignment by its name or gitlab url
@@ -58,18 +58,25 @@ class AssignmentRoutes implements RoutesManager {
        const assignment: Assignment | undefined = req.boundParams.assignment;
        const assignment: Assignment | undefined = req.boundParams.assignment;


        if ( assignment && !assignment.published && !await AssignmentManager.isUserAllowedToAccessAssignment(assignment, req.session.profile) ) {
        if ( assignment && !assignment.published && !await AssignmentManager.isUserAllowedToAccessAssignment(assignment, req.session.profile) ) {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.gitlabId;
            delete assignment.gitlabId;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.gitlabLink;
            delete assignment.gitlabLink;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.gitlabCreationInfo;
            delete assignment.gitlabCreationInfo;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.gitlabLastInfo;
            delete assignment.gitlabLastInfo;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.gitlabLastInfoDate;
            delete assignment.gitlabLastInfoDate;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.staff;
            delete assignment.staff;
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            // @ts-ignore
            delete assignment.exercises;
            delete assignment.exercises;
        }
        }
@@ -160,6 +167,14 @@ class AssignmentRoutes implements RoutesManager {
        }
        }
    }
    }


    private async publishAssignment(req: express.Request, res: express.Response) {
        return this.changeAssignmentPublishedStatus(true)(req, res);
    }

    private async unpublishAssignment(req: express.Request, res: express.Response) {
        return this.changeAssignmentPublishedStatus(false)(req, res);
    }

    private changeAssignmentPublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> {
    private changeAssignmentPublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> {
        return async (req: express.Request, res: express.Response): Promise<void> => {
        return async (req: express.Request, res: express.Response): Promise<void> => {
            if ( publish ) {
            if ( publish ) {
Original line number Original line Diff line number Diff line
@@ -6,8 +6,16 @@ import RoutesManager from '../express/RoutesManager';


class BaseRoutes implements RoutesManager {
class BaseRoutes implements RoutesManager {
    registerOnBackend(backend: Express) {
    registerOnBackend(backend: Express) {
        backend.get('/', (req: express.Request, res: express.Response) => { res.status(StatusCodes.OK).end(); });
        backend.get('/', this.homepage);
        backend.get('/health_check', (req: express.Request, res: express.Response) => { res.status(StatusCodes.OK).end(); });
        backend.get('/health_check', this.healthCheck);
    }

    private async homepage(req: express.Request, res: express.Response) {
        return req.session.sendResponse(res, StatusCodes.OK);
    }

    private async healthCheck(req: express.Request, res: express.Response) {
        return req.session.sendResponse(res, StatusCodes.OK);
    }
    }
}
}


Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ import AssignmentFile from '../shared/types/Dojo/AssignmentFile';
import ExerciseResultsFile       from '../shared/types/Dojo/ExerciseResultsFile';
import ExerciseResultsFile       from '../shared/types/Dojo/ExerciseResultsFile';
import DojoStatusCode            from '../shared/types/Dojo/DojoStatusCode';
import DojoStatusCode            from '../shared/types/Dojo/DojoStatusCode';
import GlobalHelper              from '../helpers/GlobalHelper';
import GlobalHelper              from '../helpers/GlobalHelper';
import { IFileDirStat }          from '../shared/helpers/recursiveFilesStats/RecursiveFilesStats';




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


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


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


    private async createResult(req: express.Request, res: express.Response) {
    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 exercise: Exercise = req.boundParams.exercise!;


        const result = await db.result.create({
        const result = await db.result.create({
Original line number Original line Diff line number Diff line
@@ -8,13 +8,13 @@ import GitlabManager from '../managers/GitlabManager';


class GitlabRoutes implements RoutesManager {
class GitlabRoutes implements RoutesManager {
    registerOnBackend(backend: Express) {
    registerOnBackend(backend: Express) {
        backend.get('/gitlab/project/:idOrNamespace/checkTemplateAccess', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.checkTemplateAccess);
        backend.get('/gitlab/project/:gitlabProjectIdOrNamespace/checkTemplateAccess', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.checkTemplateAccess);
    }
    }


    private async checkTemplateAccess(req: express.Request, res: express.Response) {
    private async checkTemplateAccess(req: express.Request, res: express.Response) {
        const idOrNamespace: string = req.params.idOrNamespace;
        const gitlabProjectIdOrNamespace: string = req.params.gitlabProjectIdOrNamespace;


        return res.status(await GitlabManager.checkTemplateAccess(idOrNamespace, req)).send();
        return res.status(await GitlabManager.checkTemplateAccess(gitlabProjectIdOrNamespace, req)).send();
    }
    }
}
}


Original line number Original line Diff line number Diff line
@@ -34,7 +34,7 @@ class SessionRoutes implements RoutesManager {
    registerOnBackend(backend: Express) {
    registerOnBackend(backend: Express) {
        backend.post('/login', ParamsValidatorMiddleware.validate(this.loginValidator), this.login);
        backend.post('/login', ParamsValidatorMiddleware.validate(this.loginValidator), this.login);
        backend.post('/refresh_tokens', ParamsValidatorMiddleware.validate(this.refreshTokensValidator), this.refreshTokens);
        backend.post('/refresh_tokens', ParamsValidatorMiddleware.validate(this.refreshTokensValidator), this.refreshTokens);
        backend.get('/test_session', SecurityMiddleware.check(true), (req: express.Request, res: express.Response) => req.session.sendResponse(res, StatusCodes.OK));
        backend.get('/test_session', SecurityMiddleware.check(true), this.testSession);
    }
    }


    private async login(req: express.Request, res: express.Response) {
    private async login(req: express.Request, res: express.Response) {
@@ -46,7 +46,7 @@ class SessionRoutes implements RoutesManager {
            const gitlabUser = await GitlabManager.getUserProfile(params.accessToken);
            const gitlabUser = await GitlabManager.getUserProfile(params.accessToken);


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


                req.session.sendResponse(res, StatusCodes.OK);
                req.session.sendResponse(res, StatusCodes.OK);
                return;
                return;
@@ -71,6 +71,10 @@ class SessionRoutes implements RoutesManager {
            req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while refresh tokens', DojoStatusCode.REFRESH_TOKENS_FAILED);
            req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while refresh tokens', DojoStatusCode.REFRESH_TOKENS_FAILED);
        }
        }
    }
    }

    private async testSession(req: express.Request, res: express.Response) {
        req.session.sendResponse(res, StatusCodes.OK);
    }
}
}




Original line number Original line Diff line number Diff line
Subproject commit 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989
Subproject commit 101cc26895eb0b5fe97e03bb96039e0cddd94391