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
  • Dojo_Project_Nguyen/backend/dojobackendapi
  • dojo_project/projects/backend/dojobackendapi
2 results
Select Git revision
  • add_route_assignments
  • ask-user-to-delete-exercises-on-duplicates
  • bedran_exercise-list
  • jw_sonar
  • jw_sonar_backup
  • main
  • update-dependencies
  • v6.0.0
  • 2.0.0
  • 2.1.0
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 3.1.0
  • 3.1.1
  • 3.1.2
  • 3.1.3
  • 3.2.0
  • 3.3.0
  • 3.4.0
  • 3.4.1
  • 3.4.2
  • 3.5.0
  • 3.5.1
  • 3.5.2
  • 3.5.3
  • 4.0.0
  • 4.1.0
  • 5.0.0
  • 5.0.1
  • 6.0.0-dev
  • v1.0.1
32 results
Show changes
Commits on Source (12)
Showing
with 141 additions and 57 deletions
......@@ -7,6 +7,8 @@ ExpressAPI/src/config/Version.ts
redoc.html
OpenAPI.yaml-r
sonarlint.xml
sonarlint/
############################ MacOS
# General
......
......@@ -15,6 +15,21 @@ variables:
WIKI_FOLDER: Wiki
DOC_CHANGELOG_FILE: ZolaApp/content/changelog/projects/103_api.md
DOC_FILE_BEGIN: |
+++
title = "Dojo Backend API"
slug = "dojo-backend-api"
weight = 103
template = "docs/page.html"
[extra]
lead = "Changelog of The Dojo Backend API."
toc = true
top = false
+++
.get_version:
script:
......@@ -74,6 +89,26 @@ code_quality:lint:
- npm run lint
code_quality:sonarqube:
stage: code_quality
tags:
- code_quality
image:
name: leadrien/isc-sonar-scanner-cli
entrypoint: [ "" ]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
rules:
- if: '$CI_COMMIT_TAG =~ "/^$/"'
test:build:
stage: test
image: node:latest
......@@ -111,7 +146,7 @@ clean:packages:
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
clean:dev:release:
clean:release:dev:
stage: clean
tags:
- gitlab_clean
......@@ -124,7 +159,7 @@ clean:dev:release:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
clean:dev:packages:
clean:packages:dev:
stage: clean
tags:
- gitlab_clean
......@@ -137,7 +172,7 @@ clean:dev:packages:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
upload:packages:wiki:
upload:packages:doc:wiki:
stage: upload
tags:
- gitlab_package
......@@ -160,7 +195,7 @@ upload:packages:wiki:
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
release:wiki:
release:doc:wiki:
stage: release
tags:
- release
......@@ -208,6 +243,47 @@ release:wiki:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
release:doc:changelog:
stage: release
tags:
- release
image: ubuntu:latest
script:
# Install dependencies
- apt-get update -y
- apt-get install -y openssh-client git unzip sshpass --fix-missing
# Add SSH key
- eval `ssh-agent -s`
- echo "$DOC_GIT_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$DOC_GIT_SSH_PUBLIC_KEY" >> ~/.ssh/id_rsa.pub
- echo "$DOC_GIT_SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
# Set git config
- git config --global user.email "dojo@minelli.me"
- git config --global user.name "[Gitlab CI/CD] ${PROJECT_NAME}"
# Pull repo
- git clone ssh://git@ssh.hesge.ch:10572/dojo_project/projects/ui/dojodoc.git
# Do something after pulling your repo
- echo "${DOC_FILE_BEGIN}$(cat CHANGELOG.md)" > dojodoc/${DOC_CHANGELOG_FILE}
# Push repo changes into current repo
- cd dojodoc
- git add ${DOC_CHANGELOG_FILE}
- git commit -m "[Gitlab CI/CD] ${PROJECT_NAME} => Automatic update of changelog" || echo "No changes, nothing to commit!"
- git push
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
release:gitlab:
stage: release
tags:
......
......@@ -18,6 +18,20 @@
-->
## 3.6.0 (???)
### ✨ Feature
- Add features related to corrige (commentary, commit specific link / update, delete link)
### 🔨 Internal / Developers
- Migration to GitBreaker library for all Gitlab API calls
- SonarQube integration
- Multi-process start is disabled where it is not in a production environment
### 📚 Documentation
- Corrige routes documentation
## 3.5.3 (2024-02-26)
### 🐛 Bugfix
......
......@@ -15,4 +15,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="SonarLintModuleSettings">
<option name="uniqueId" value="2749ea0f-74a8-42c0-9fd6-d6a4b4cd75a4" />
</component>
</module>
\ No newline at end of file
openapi: 3.1.0
info:
title: Dojo API
version: 3.5.3
version: 3.6.0
description: |
**Backend API of the Dojo project.**
......
{
"name": "dojo_backend_api",
"version": "3.5.3",
"version": "3.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dojo_backend_api",
"version": "3.5.3",
"version": "3.6.0",
"license": "AGPLv3",
"dependencies": {
"@gitbeaker/rest": "^39.34.2",
......
{
"name" : "dojo_backend_api",
"description" : "Backend API of the Dojo project",
"version" : "3.5.3",
"version" : "3.6.0",
"license" : "AGPLv3",
"author" : "Michaël Minelli <dojo@minelli.me>",
"main" : "dist/src/app.js",
......
......@@ -16,7 +16,7 @@ async function main() {
main().then(async () => {
await db.$disconnect();
}).catch(async (e) => {
}).catch(async e => {
logger.error(e);
await db.$disconnect();
process.exit(1);
......
......@@ -13,7 +13,5 @@ HttpManager.registerAxiosInterceptor();
role : WorkerRole.API,
quantity : ClusterManager.CORES,
restartOnFail: true,
loadTask : () => {
return new API();
}
loadTask : () => new API()
} ])).run();
......@@ -19,7 +19,7 @@ class Config {
version: {
[client: string]: string
}
}; // { version: { CLIENT: CONDITION } }
};
public readonly dojoCLI: {
versionUpdatePeriodMs: number
......
......@@ -19,12 +19,9 @@ class Session {
this._profile = newProfile;
}
constructor() { }
async initSession(req: express.Request, res: express.Response) {
const authorization = req.headers.authorization;
if ( authorization ) {
if ( authorization.startsWith('Bearer ') ) {
if ( authorization && authorization.startsWith('Bearer ') ) {
const jwtToken = authorization.replace('Bearer ', '');
try {
......@@ -39,10 +36,10 @@ class Session {
}
}
}
}
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 } : {});
const options = Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {};
return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, options);
}
private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoBackendResponse<T>> {
......
......@@ -38,14 +38,16 @@ class API implements WorkerTask {
private initBaseMiddlewares() {
this.backend.use(multer({
limits: { fieldSize: 100 * 1024 * 1024 }
limits: {
fieldSize: 15728640 // 15MB
}
}).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
this.backend.use(morganMiddleware); //Log API accesses
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(async (req, res, next) => {
this.backend.use(async (_req, res, next) => {
res.header('dojocli-latest-version', await DojoCliVersionHelper.getLatestVersion());
next();
});
......@@ -59,9 +61,9 @@ class API implements WorkerTask {
url: '../OpenAPI.yaml'
}
};
this.backend.get('/docs/OpenAPI.yaml', (req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/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')));
this.backend.get('/docs/redoc.html', (_req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/redoc.html')));
this.backend.get('/docs/', (req, res) => {
const prefix = req.url.slice(-1) === '/' ? '' : 'docs/';
......
......@@ -31,7 +31,7 @@ prisma.$on('warn', e => logger.warn(`Prisma => ${ e.message }`));
prisma.$on('error', e => logger.error(`Prisma => ${ e.message }`));
const db = prisma.$extends(UserQueryExtension).$extends(UserResultExtension).$extends(AssignmentResultExtension).$extends(ExerciseResultExtension);
const DatabaseHelper = prisma.$extends(UserQueryExtension).$extends(UserResultExtension).$extends(AssignmentResultExtension).$extends(ExerciseResultExtension);
export default db;
\ No newline at end of file
export default DatabaseHelper;
\ No newline at end of file
......@@ -7,8 +7,6 @@ class DojoCliVersionHelper {
private latestUpdate: Date | undefined;
private latestVersion: string | undefined;
constructor() { }
private async updateVersion(): Promise<void> {
const releases: Array<GitlabRelease> = await GitlabManager.getRepositoryReleases(Config.dojoCLI.repositoryId);
for ( const release of releases ) {
......
......@@ -9,8 +9,7 @@ class DojoModelsHelper {
* @param depth The depth of the search for LazyVal instances
*/
async getFullSerializableObject<T extends NonNullable<unknown>>(obj: T, depth: number = 0): Promise<unknown> {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const result: any = {};
const result: { [key: string]: unknown } = {};
for ( const key in obj ) {
let value: unknown = obj[key];
......
......@@ -31,9 +31,9 @@ class DojoValidators {
}
readonly nullSanitizer = this.toValidatorSchemaOptions({
options: (value) => {
options: value => {
try {
return value == 'null' || value == 'undefined' || value == '' ? null : value;
return value === 'null' || value === 'undefined' || value === '' ? null : value;
} catch ( error ) {
logger.error(`null sanitizer error: ${ error }`);
......@@ -43,7 +43,7 @@ class DojoValidators {
});
readonly jsonSanitizer = this.toValidatorSchemaOptions({
options: (value) => {
options: value => {
try {
return JSON.parse(value as string);
} catch ( e ) {
......@@ -62,7 +62,7 @@ class DojoValidators {
return new Promise((resolve, reject) => {
const template = this.getParamValue(req, path) as string;
if ( template ) {
GitlabManager.checkTemplateAccess(template, req).then((templateAccess) => {
GitlabManager.checkTemplateAccess(template, req).then(templateAccess => {
templateAccess !== StatusCodes.OK ? reject() : resolve(true);
});
}
......@@ -79,7 +79,8 @@ class DojoValidators {
try {
const template = this.getParamValue(req, path);
if ( template ) {
return `${ Config.gitlab.urls[0].replace(/^([a-z]{3,5}:\/{2})?(.*)/, `$1${ Config.gitlab.account.username }:${ Config.gitlab.account.token }@$2`) }${ template }.git`;
const gitlabUrlWithCredentials = Config.gitlab.urls[0].replace(/^([a-z]{3,5}:\/{2})?(.*)/, `$1${ Config.gitlab.account.username }:${ Config.gitlab.account.token }@$2`);
return `${ gitlabUrlWithCredentials }${ template }.git`;
} else {
return Config.assignment.default.template;
}
......@@ -121,7 +122,7 @@ class DojoValidators {
if ( exerciseIdOrUrl ) {
ParamsCallbackManager.initBoundParams(req);
ExerciseManager.get(exerciseIdOrUrl).then((exercise) => {
ExerciseManager.get(exerciseIdOrUrl).then(exercise => {
req.boundParams.exercise = exercise;
exercise !== undefined ? resolve(true) : reject();
......
......@@ -16,9 +16,9 @@ class GlobalHelper {
if ( repositoryToRemove ) {
await GitlabManager.deleteRepository(repositoryToRemove.id);
}
} catch ( error ) {
} catch ( deleteError ) {
logger.error('Repository deletion error');
logger.error(error);
logger.error(deleteError);
}
if ( error instanceof AxiosError ) {
......
......@@ -30,9 +30,7 @@ export default Prisma.defineExtension(client => {
assignment: {
corrections: {
compute(assignment) {
return new LazyVal<Array<Partial<Exercise>> | undefined>(() => {
return getCorrections(assignment);
});
return new LazyVal<Array<Partial<Exercise>> | undefined>(() => getCorrections(assignment));
}
}
}
......
......@@ -13,7 +13,7 @@ export default Prisma.defineExtension(client => {
role: true
},
compute(user) {
return user.role == UserRole.TEACHING_STAFF || user.role == UserRole.ADMIN;
return user.role === UserRole.TEACHING_STAFF || user.role === UserRole.ADMIN;
}
},
isAdmin : {
......@@ -21,14 +21,12 @@ export default Prisma.defineExtension(client => {
role: true
},
compute(user) {
return user.role == UserRole.ADMIN;
return user.role === UserRole.ADMIN;
}
},
gitlabProfile : {
compute(user) {
return new LazyVal<GitlabUser | undefined>(() => {
return GitlabManager.getUserById(user.id);
});
return new LazyVal<GitlabUser | undefined>(() => GitlabManager.getUserById(user.id));
}
}
}
......
......@@ -3,12 +3,10 @@ import logger from '../shared/logging/WinstonLogger';
const stream: StreamOptions = {
write: (message) => logger.http(message)
write: message => logger.http(message)
};
const skip = () => {
return false; //SharedConfig.production;
};
const skip = () => false;
const morganMiddleware = morgan(':method :url :status :res[content-length] - :response-time ms', {
stream,
......