merge: Merge conflicts opgelost voor mergen met dev
This commit is contained in:
commit
4f0c4241b6
74 changed files with 2193 additions and 633 deletions
|
@ -1,15 +1,24 @@
|
|||
#
|
||||
# Basic configuration
|
||||
# Development environment configuration
|
||||
#
|
||||
# You probably don't need to change these values, as this configuration takes
|
||||
# the docker services and their default ports into account.
|
||||
#
|
||||
|
||||
DWENGO_PORT=3000 # The port the backend will listen on
|
||||
### Dwengo ###
|
||||
|
||||
#DWENGO_PORT=3000
|
||||
#DWENGO_LEARNING_CONTENT_REPO_API_BASE_URL=https://dwengo.org/backend/api
|
||||
#DWENGO_FALLBACK_LANGUAGE=nl
|
||||
#DWENGO_RUN_MODE=dev
|
||||
|
||||
DWENGO_DB_HOST=localhost
|
||||
DWENGO_DB_PORT=5431
|
||||
#DWENGO_DB_NAME=dwengo
|
||||
DWENGO_DB_USERNAME=postgres
|
||||
DWENGO_DB_PASSWORD=postgres
|
||||
DWENGO_DB_UPDATE=true
|
||||
|
||||
# Auth
|
||||
#DWENGO_DB_CONTENT_PREFIX=u_
|
||||
|
||||
DWENGO_AUTH_STUDENT_URL=http://localhost:7080/realms/student
|
||||
DWENGO_AUTH_STUDENT_CLIENT_ID=dwengo
|
||||
|
@ -17,12 +26,12 @@ DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://localhost:7080/realms/student/protocol/
|
|||
DWENGO_AUTH_TEACHER_URL=http://localhost:7080/realms/teacher
|
||||
DWENGO_AUTH_TEACHER_CLIENT_ID=dwengo
|
||||
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs
|
||||
#DWENGO_AUTH_AUDIENCE=account
|
||||
|
||||
# Allow Vite dev-server to access the backend (for testing purposes). Don't forget to remove this in production!
|
||||
DWENGO_CORS_ALLOWED_ORIGINS=http://localhost:5173
|
||||
#DWENGO_CORS_ALLOWED_HEADERS=Authorization,Content-Type
|
||||
|
||||
#
|
||||
# Advanced configuration
|
||||
#
|
||||
### Advanced configuration ###
|
||||
|
||||
# LOKI_HOST=http://localhost:9001 # The address of the Loki instance, used for logging
|
||||
DWENGO_LOGGING_LEVEL=debug
|
||||
#DWENGO_LOGGING_LOKI_HOST=http://localhost:3102
|
||||
|
|
|
@ -1,27 +1,68 @@
|
|||
#
|
||||
# Basic configuration
|
||||
#
|
||||
# Change the values of the variables below to match your environment!
|
||||
# Default values are commented out.
|
||||
#
|
||||
|
||||
DWENGO_PORT=3000 # The port the backend will listen on
|
||||
### Dwengo ###
|
||||
|
||||
# Port the backend will listen on
|
||||
#DWENGO_PORT=3000
|
||||
# The hostname or IP address of the remote learning content API.
|
||||
#DWENGO_LEARNING_CONTENT_REPO_API_BASE_URL=https://dwengo.org/backend/api
|
||||
# The default fallback language.
|
||||
#DWENGO_FALLBACK_LANGUAGE=nl
|
||||
# Whether running in production mode or not. Possible values are "prod" or "dev".
|
||||
#DWENGO_RUN_MODE=dev
|
||||
|
||||
# ! Change this! The hostname or IP address of the database
|
||||
# If running your stack in docker, this should use the docker service name.
|
||||
DWENGO_DB_HOST=domain-or-ip-of-database
|
||||
DWENGO_DB_PORT=5431
|
||||
|
||||
# Change this to the actual credentials of the user Dwengo should use in the backend
|
||||
DWENGO_DB_USERNAME=postgres
|
||||
DWENGO_DB_PASSWORD=postgres
|
||||
|
||||
# The port of the database.
|
||||
#DWENGO_DB_PORT=5432
|
||||
# The name of the database.
|
||||
#DWENGO_DB_NAME=dwengo
|
||||
# ! Change this! The username of the database user.
|
||||
DWENGO_DB_USERNAME=username
|
||||
# ! Change this! The password of the database user.
|
||||
DWENGO_DB_PASSWORD=password
|
||||
# Whether the database scheme needs to be updated.
|
||||
# Set this to true when the database scheme needs to be updated. In that case, take a backup first.
|
||||
DWENGO_DB_UPDATE=false
|
||||
#DWENGO_DB_UPDATE=false
|
||||
# The prefix used for custom user content.
|
||||
#DWENGO_DB_CONTENT_PREFIX=u_
|
||||
|
||||
# Data for the identity provider via which the students authenticate.
|
||||
DWENGO_AUTH_STUDENT_URL=http://localhost:7080/realms/student
|
||||
# ! Change this! The external URL for student authentication. Should be reachable by the client.
|
||||
# E.g. https://sel2-1.ugent.be/idp/realms/student
|
||||
DWENGO_AUTH_STUDENT_URL=http://hostname/idp/realms/student
|
||||
# ! Change this! The client ID for student authentication.
|
||||
DWENGO_AUTH_STUDENT_CLIENT_ID=dwengo
|
||||
DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://localhost:7080/realms/student/protocol/openid-connect/certs
|
||||
|
||||
# Data for the identity provider via which the teachers authenticate.
|
||||
DWENGO_AUTH_TEACHER_URL=http://localhost:7080/realms/teacher
|
||||
# ! Change this! The internal URL for retrieving the JWKS for student authentication.
|
||||
# Should be reachable by the backend. If running your stack in docker, this should use the docker service name.
|
||||
# E.g. http://idp:7080/realms/student/protocol/openid-connect/certs
|
||||
DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://hostname/realms/student/protocol/openid-connect/certs
|
||||
# ! Change this! The external URL for teacher authentication. Should be reachable by the client.
|
||||
# E.g. https://sel2-1.ugent.be/idp/realms/teacher
|
||||
DWENGO_AUTH_TEACHER_URL=http://hostname/idp/realms/teacher
|
||||
# ! Change this! The client ID for teacher authentication.
|
||||
DWENGO_AUTH_TEACHER_CLIENT_ID=dwengo
|
||||
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs
|
||||
# ! Change this! The internal URL for retrieving the JWKS for teacher authentication.
|
||||
# Should be reachable by the backend. If running your stack in docker, this should use the docker service name.
|
||||
# E.g. http://idp:7080/realms/teacher/protocol/openid-connect/certs
|
||||
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://hostname/realms/teacher/protocol/openid-connect/certs
|
||||
# The IDP audience
|
||||
#DWENGO_AUTH_AUDIENCE=account
|
||||
|
||||
# The address of the Lokiinstance, used for logging
|
||||
# LOKI_HOST=http://localhost:3102
|
||||
# Allowed origins for CORS requests. Separate multiple origins with a comma.
|
||||
#DWENGO_CORS_ALLOWED_ORIGINS=
|
||||
# Allowed headers for CORS requests. Separate multiple headers with a comma.
|
||||
#DWENGO_CORS_ALLOWED_HEADERS=Authorization,Content-Type
|
||||
|
||||
### Advanced configuration ###
|
||||
|
||||
# The logging level. Possible values are "debug", "info", "warn", "error".
|
||||
#DWENGO_LOGGING_LEVEL=info
|
||||
# The address of the Loki instance, a log aggregation system.
|
||||
# If running your stack in docker, this should use the docker service name.
|
||||
#DWENGO_LOGGING_LOKI_HOST=http://localhost:3102
|
||||
|
|
|
@ -1,28 +1,37 @@
|
|||
DWENGO_PORT=3000 # The port the backend will listen on
|
||||
DWENGO_DB_HOST=db # Name of the database container
|
||||
DWENGO_DB_PORT=5431
|
||||
#
|
||||
# Production environment configuration
|
||||
#
|
||||
# Change the values of the variables below to match your production environment!
|
||||
# See .env.example for more information.
|
||||
#
|
||||
|
||||
# Change this to the actual credentials of the user Dwengo should use in the backend
|
||||
### Dwengo ###
|
||||
|
||||
DWENGO_PORT=3000
|
||||
#DWENGO_LEARNING_CONTENT_REPO_API_BASE_URL=https://dwengo.org/backend/api
|
||||
#DWENGO_FALLBACK_LANGUAGE=nl
|
||||
DWENGO_RUN_MODE=prod
|
||||
|
||||
DWENGO_DB_HOST=db
|
||||
DWENGO_DB_PORT=5432
|
||||
DWENGO_DB_NAME=postgres
|
||||
DWENGO_DB_USERNAME=postgres
|
||||
DWENGO_DB_PASSWORD=postgres
|
||||
|
||||
# Set this to true when the database scheme needs to be updated. In that case, take a backup first.
|
||||
DWENGO_DB_UPDATE=false
|
||||
#DWENGO_DB_CONTENT_PREFIX=u_
|
||||
|
||||
# Data for the identity provider via which the students authenticate.
|
||||
DWENGO_AUTH_STUDENT_URL=https://sel2-1.ugent.be/idp/realms/student
|
||||
DWENGO_AUTH_STUDENT_CLIENT_ID=dwengo
|
||||
DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://idp:7080/idp/realms/student/protocol/openid-connect/certs # Name of the idp container
|
||||
# Data for the identity provider via which the teachers authenticate.
|
||||
DWENGO_AUTH_TEACHER_URL=https://sel2-1.ugent.be/idp/realms/teacher
|
||||
DWENGO_AUTH_TEACHER_CLIENT_ID=dwengo
|
||||
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://idp:7080/idp/realms/teacher/protocol/openid-connect/certs # Name of the idp container
|
||||
#DWENGO_AUTH_AUDIENCE=account
|
||||
|
||||
#
|
||||
# Advanced configuration
|
||||
#
|
||||
#DWENGO_CORS_ALLOWED_ORIGINS=
|
||||
#DWENGO_CORS_ALLOWED_HEADERS=Authorization,Content-Type
|
||||
|
||||
# Logging and monitoring
|
||||
### Advanced configuration ###
|
||||
|
||||
# LOKI_HOST=http://logging:3102 # The address of the Loki instance, used for logging
|
||||
DWENGO_LOGGING_LEVEL=info
|
||||
DWENGO_LOGGING_LOKI_HOST=http://logging:3102
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
PORT=3000
|
||||
DWENGO_DB_UPDATE=true
|
||||
#
|
||||
# Test environment configuration
|
||||
#
|
||||
# Should not need to be modified.
|
||||
# See .env.example for more information.
|
||||
#
|
||||
|
||||
### Dwengo ###
|
||||
|
||||
DWENGO_PORT=3000
|
||||
|
||||
DWENGO_DB_NAME=":memory:"
|
||||
DWENGO_DB_UPDATE=true
|
||||
|
|
|
@ -30,6 +30,7 @@ COPY package-lock.json backend/package.json ./
|
|||
RUN npm install --silent --only=production
|
||||
|
||||
COPY ./docs /docs
|
||||
COPY ./backend/i18n /app/i18n
|
||||
COPY --from=build-stage /app/backend/dist ./dist/
|
||||
|
||||
EXPOSE 3000
|
||||
|
|
|
@ -4,23 +4,24 @@
|
|||
|
||||
```shell
|
||||
npm install
|
||||
|
||||
# Start de nodige services voor ontwikkeling
|
||||
cd ../ # Ga naar de root van de repository
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Setup the environment variables in a `.env` file in the root of the project. You can use the `.env.example` file as a template.
|
||||
Zet de omgevingsvariabelen in een `.env` bestand in de root van het project.
|
||||
Je kan het `.env.example` bestand als template gebruiken.
|
||||
|
||||
### Development
|
||||
### Ontwikkeling
|
||||
|
||||
```shell
|
||||
# Omgevingsvariabelen
|
||||
cp .env.development.example .env.development.local
|
||||
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
```shell
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
Voer volgend commando uit om de unit tests uit te voeren:
|
||||
|
@ -29,6 +30,18 @@ Voer volgend commando uit om de unit tests uit te voeren:
|
|||
npm run test:unit
|
||||
```
|
||||
|
||||
### Productie
|
||||
|
||||
```shell
|
||||
# Omgevingsvariabelen
|
||||
cp .env.development.example .env
|
||||
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
Zie ook de [installatiehandleiding](https://github.com/SELab-2/Dwengo-1/wiki/Administrator:-Productie-omgeving).
|
||||
|
||||
## Keycloak configuratie
|
||||
|
||||
Tijdens development is het voldoende om gebruik te maken van de keycloak configuratie die automatisch ingeladen wordt.
|
||||
|
|
|
@ -28,9 +28,9 @@ curricula_page:
|
|||
contact: ''
|
||||
teaser: https://www.youtube.com/embed/2B6gZ9HdQ1Y
|
||||
basics_ai:
|
||||
title: Basisprincipes van AI
|
||||
sub_title: Basisprincipes van AI
|
||||
description: 'Onder dit lesthema bundelen we verschillende activiteiten waarin de basisprincipes van artificiële intelligentie (AI) aan bod komen. Leerlingen leren wat AI is, hoe het werkt en hoe het kan worden toegepast in verschillende domeinen.'
|
||||
title: Grundlagen der KI
|
||||
sub_title: Grundlagen der KI
|
||||
description: 'Dieses Thema bündelt verschiedene Aktivitäten, in denen die grundlegenden Prinzipien der künstlichen Intelligenz (KI) behandelt werden. Die Schüler lernen, was KI ist, wie sie funktioniert und wie sie in verschiedenen Bereichen angewendet werden kann.'
|
||||
contact: ''
|
||||
kiks:
|
||||
title: KI und Klima
|
|
@ -28,10 +28,11 @@ curricula_page:
|
|||
contact: ''
|
||||
teaser: https://www.youtube.com/embed/2B6gZ9HdQ1Y
|
||||
basics_ai:
|
||||
title: Basisprincipes van AI
|
||||
sub_title: Basisprincipes van AI
|
||||
description: 'Onder dit lesthema bundelen we verschillende activiteiten waarin de basisprincipes van artificiële intelligentie (AI) aan bod komen. Leerlingen leren wat AI is, hoe het werkt en hoe het kan worden toegepast in verschillende domeinen.'
|
||||
title: Basics of AI
|
||||
sub_title: Basics of AI
|
||||
description: 'This theme brings together various activities covering the basic principles of Artificial Intelligence (AI). Students learn what AI is, how it works, and how it can be applied in different domains.'
|
||||
contact: ''
|
||||
|
||||
kiks:
|
||||
title: AI and Climate
|
||||
sub_title: KIKS
|
|
@ -28,9 +28,9 @@ curricula_page:
|
|||
contact: ''
|
||||
teaser: https://www.youtube.com/embed/2B6gZ9HdQ1Y
|
||||
basics_ai:
|
||||
title: Basisprincipes van AI
|
||||
sub_title: Basisprincipes van AI
|
||||
description: 'Onder dit lesthema bundelen we verschillende activiteiten waarin de basisprincipes van artificiële intelligentie (AI) aan bod komen. Leerlingen leren wat AI is, hoe het werkt en hoe het kan worden toegepast in verschillende domeinen.'
|
||||
title: Principes de base de l’IA
|
||||
sub_title: Principes de base de l’IA
|
||||
description: 'Ce thème rassemble différentes activités portant sur les principes fondamentaux de l’intelligence artificielle (IA). Les élèves apprennent ce qu’est l’IA, comment elle fonctionne et comment elle peut être appliquée dans divers domaines.'
|
||||
contact: ''
|
||||
kiks:
|
||||
title: 'IA et changement climatique'
|
|
@ -1,12 +1,7 @@
|
|||
import { EnvVars, getEnvVar } from './util/envvars.js';
|
||||
import { Language } from './entities/content/language.js';
|
||||
|
||||
// API
|
||||
export const DWENGO_API_BASE = getEnvVar(EnvVars.LearningContentRepoApiBaseUrl);
|
||||
export const FALLBACK_LANG = getEnvVar(EnvVars.FallbackLanguage);
|
||||
|
||||
// Logging
|
||||
export const LOG_LEVEL: string = 'development' === process.env.NODE_ENV ? 'debug' : 'info';
|
||||
export const LOKI_HOST: string = process.env.LOKI_HOST || 'http://localhost:3102';
|
||||
|
||||
export const FALLBACK_SEQ_NUM = 1;
|
||||
|
|
|
@ -32,7 +32,7 @@ export async function createAssignmentHandler(req: Request, res: Response): Prom
|
|||
return;
|
||||
}
|
||||
|
||||
res.status(201).json({ assignment: assignment });
|
||||
res.status(201).json(assignment);
|
||||
}
|
||||
|
||||
export async function getAssignmentHandler(req: Request, res: Response): Promise<void> {
|
||||
|
@ -57,13 +57,14 @@ export async function getAssignmentHandler(req: Request, res: Response): Promise
|
|||
export async function getAssignmentsSubmissionsHandler(req: Request, res: Response): Promise<void> {
|
||||
const classid = req.params.classid;
|
||||
const assignmentNumber = +req.params.id;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (isNaN(assignmentNumber)) {
|
||||
res.status(400).json({ error: 'Assignment id must be a number' });
|
||||
return;
|
||||
}
|
||||
|
||||
const submissions = await getAssignmentsSubmissions(classid, assignmentNumber);
|
||||
const submissions = await getAssignmentsSubmissions(classid, assignmentNumber, full);
|
||||
|
||||
res.json({
|
||||
submissions: submissions,
|
||||
|
|
|
@ -28,30 +28,19 @@ export async function createClassHandler(req: Request, res: Response): Promise<v
|
|||
return;
|
||||
}
|
||||
|
||||
res.status(201).json({ class: cls });
|
||||
res.status(201).json(cls);
|
||||
}
|
||||
|
||||
export async function getClassHandler(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const classId = req.params.id;
|
||||
const cls = await getClass(classId);
|
||||
const classId = req.params.id;
|
||||
const cls = await getClass(classId);
|
||||
|
||||
if (!cls) {
|
||||
res.status(404).json({ error: 'Class not found' });
|
||||
return;
|
||||
}
|
||||
cls.endpoints = {
|
||||
self: `${req.baseUrl}/${req.params.id}`,
|
||||
invitations: `${req.baseUrl}/${req.params.id}/invitations`,
|
||||
assignments: `${req.baseUrl}/${req.params.id}/assignments`,
|
||||
students: `${req.baseUrl}/${req.params.id}/students`,
|
||||
};
|
||||
|
||||
res.json(cls);
|
||||
} catch (error) {
|
||||
console.error('Error fetching learning objects:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
if (!cls) {
|
||||
res.status(404).json({ error: 'Class not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(cls);
|
||||
}
|
||||
|
||||
export async function getClassStudentsHandler(req: Request, res: Response): Promise<void> {
|
||||
|
@ -72,7 +61,7 @@ export async function getClassStudentsHandler(req: Request, res: Response): Prom
|
|||
|
||||
export async function getTeacherInvitationsHandler(req: Request, res: Response): Promise<void> {
|
||||
const classId = req.params.id;
|
||||
const full = req.query.full === 'true'; // TODO: not implemented yet
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const invitations = await getClassTeacherInvitations(classId, full);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ export async function getGroupHandler(req: Request, res: Response): Promise<void
|
|||
|
||||
if (!group) {
|
||||
res.status(404).json({ error: 'Group not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(group);
|
||||
|
@ -63,12 +64,12 @@ export async function createGroupHandler(req: Request, res: Response): Promise<v
|
|||
return;
|
||||
}
|
||||
|
||||
res.status(201).json({ group: group });
|
||||
res.status(201).json(group);
|
||||
}
|
||||
|
||||
export async function getGroupSubmissionsHandler(req: Request, res: Response): Promise<void> {
|
||||
const classId = req.params.classid;
|
||||
// Const full = req.query.full === 'true';
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const assignmentId = +req.params.assignmentid;
|
||||
|
||||
|
@ -84,7 +85,7 @@ export async function getGroupSubmissionsHandler(req: Request, res: Response): P
|
|||
return;
|
||||
}
|
||||
|
||||
const submissions = await getGroupSubmissions(classId, assignmentId, groupId);
|
||||
const submissions = await getGroupSubmissions(classId, assignmentId, groupId, full);
|
||||
|
||||
res.json({
|
||||
submissions: submissions,
|
||||
|
|
|
@ -40,7 +40,7 @@ export async function getAllLearningObjects(req: Request, res: Response): Promis
|
|||
learningObjects = await learningObjectService.getLearningObjectIdsFromPath(learningPathId);
|
||||
}
|
||||
|
||||
res.json(learningObjects);
|
||||
res.json({ learningObjects: learningObjects });
|
||||
}
|
||||
|
||||
export async function getLearningObject(req: Request, res: Response): Promise<void> {
|
||||
|
|
|
@ -48,7 +48,7 @@ export async function getAllQuestionsHandler(req: Request, res: Response): Promi
|
|||
if (!questions) {
|
||||
res.status(404).json({ error: `Questions not found.` });
|
||||
} else {
|
||||
res.json(questions);
|
||||
res.json({ questions: questions });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,12 +76,12 @@ export async function getQuestionAnswersHandler(req: Request, res: Response): Pr
|
|||
return;
|
||||
}
|
||||
|
||||
const answers = getAnswersByQuestion(questionId, full);
|
||||
const answers = await getAnswersByQuestion(questionId, full);
|
||||
|
||||
if (!answers) {
|
||||
res.status(404).json({ error: `Questions not found.` });
|
||||
res.status(404).json({ error: `Questions not found` });
|
||||
} else {
|
||||
res.json(answers);
|
||||
res.json({ answers: answers });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ export async function createQuestionHandler(req: Request, res: Response): Promis
|
|||
const question = await createQuestion(questionDTO);
|
||||
|
||||
if (!question) {
|
||||
res.status(400).json({ error: 'Could not add question' });
|
||||
res.status(400).json({ error: 'Could not create question' });
|
||||
} else {
|
||||
res.json(question);
|
||||
}
|
||||
|
|
|
@ -9,29 +9,21 @@ import {
|
|||
getStudentGroups,
|
||||
getStudentSubmissions,
|
||||
} from '../services/students.js';
|
||||
import { ClassDTO } from '../interfaces/class.js';
|
||||
import { getAllAssignments } from '../services/assignments.js';
|
||||
import { getUserHandler } from './users.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { StudentDTO } from '../interfaces/student.js';
|
||||
import { getStudentRepository } from '../data/repositories.js';
|
||||
import { UserDTO } from '../interfaces/user.js';
|
||||
|
||||
// TODO: accept arguments (full, ...)
|
||||
// TODO: endpoints
|
||||
export async function getAllStudentsHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const studentRepository = getStudentRepository();
|
||||
|
||||
const students: StudentDTO[] | string[] = full ? await getAllStudents() : await getAllStudents();
|
||||
const students = await getAllStudents(full);
|
||||
|
||||
if (!students) {
|
||||
res.status(404).json({ error: `Student not found.` });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(students);
|
||||
res.json({ students: students });
|
||||
}
|
||||
|
||||
export async function getStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
|
@ -51,7 +43,7 @@ export async function getStudentHandler(req: Request, res: Response): Promise<vo
|
|||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(user);
|
||||
res.json(user);
|
||||
}
|
||||
|
||||
export async function createStudentHandler(req: Request, res: Response) {
|
||||
|
@ -65,6 +57,14 @@ export async function createStudentHandler(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
const newUser = await createStudent(userData);
|
||||
|
||||
if (!newUser) {
|
||||
res.status(500).json({
|
||||
error: 'Something went wrong while creating student'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(newUser);
|
||||
}
|
||||
|
||||
|
@ -88,25 +88,14 @@ export async function deleteStudentHandler(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
export async function getStudentClassesHandler(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.id;
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.id;
|
||||
|
||||
const classes = await getStudentClasses(username, full);
|
||||
const classes = await getStudentClasses(username, full);
|
||||
|
||||
res.json({
|
||||
classes: classes,
|
||||
endpoints: {
|
||||
self: `${req.baseUrl}/${req.params.id}`,
|
||||
classes: `${req.baseUrl}/${req.params.id}/invitations`,
|
||||
questions: `${req.baseUrl}/${req.params.id}/assignments`,
|
||||
students: `${req.baseUrl}/${req.params.id}/students`,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching learning objects:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
res.json({
|
||||
classes: classes,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
@ -137,8 +126,9 @@ export async function getStudentGroupsHandler(req: Request, res: Response): Prom
|
|||
|
||||
export async function getStudentSubmissionsHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.id;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const submissions = await getStudentSubmissions(username);
|
||||
const submissions = await getStudentSubmissions(username, full);
|
||||
|
||||
res.json({
|
||||
submissions: submissions,
|
||||
|
|
|
@ -43,10 +43,11 @@ export async function createSubmissionHandler(req: Request, res: Response) {
|
|||
const submission = await createSubmission(submissionDTO);
|
||||
|
||||
if (!submission) {
|
||||
res.status(404).json({ error: 'Submission not added' });
|
||||
} else {
|
||||
res.json(submission);
|
||||
res.status(400).json({ error: 'Failed to create submission' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(submission);
|
||||
}
|
||||
|
||||
export async function deleteSubmissionHandler(req: Request, res: Response) {
|
||||
|
@ -60,7 +61,8 @@ export async function deleteSubmissionHandler(req: Request, res: Response) {
|
|||
|
||||
if (!submission) {
|
||||
res.status(404).json({ error: 'Submission not found' });
|
||||
} else {
|
||||
res.json(submission);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(submission);
|
||||
}
|
||||
|
|
|
@ -4,33 +4,23 @@ import {
|
|||
deleteTeacher,
|
||||
getAllTeachers,
|
||||
getClassesByTeacher,
|
||||
getClassIdsByTeacher,
|
||||
getQuestionIdsByTeacher,
|
||||
getQuestionsByTeacher,
|
||||
getStudentIdsByTeacher,
|
||||
getStudentsByTeacher,
|
||||
getTeacher,
|
||||
} from '../services/teachers.js';
|
||||
import { ClassDTO } from '../interfaces/class.js';
|
||||
import { StudentDTO } from '../interfaces/student.js';
|
||||
import { QuestionDTO, QuestionId } from '../interfaces/question.js';
|
||||
import { Teacher } from '../entities/users/teacher.entity.js';
|
||||
import { TeacherDTO } from '../interfaces/teacher.js';
|
||||
import { getTeacherRepository } from '../data/repositories.js';
|
||||
|
||||
export async function getAllTeachersHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const teacherRepository = getTeacherRepository();
|
||||
|
||||
const teachers: TeacherDTO[] | string[] = full ? await getAllTeachers() : await getAllTeachers();
|
||||
const teachers = await getAllTeachers(full);
|
||||
|
||||
if (!teachers) {
|
||||
res.status(404).json({ error: `Teacher not found.` });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(teachers);
|
||||
res.json({ teachers: teachers });
|
||||
}
|
||||
|
||||
export async function getTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||
|
@ -45,12 +35,12 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo
|
|||
|
||||
if (!user) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
error: `Teacher '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(user);
|
||||
res.json(user);
|
||||
}
|
||||
|
||||
export async function createTeacherHandler(req: Request, res: Response) {
|
||||
|
@ -64,6 +54,12 @@ export async function createTeacherHandler(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
const newUser = await createTeacher(userData);
|
||||
|
||||
if (!newUser) {
|
||||
res.status(400).json({ error: 'Failed to create teacher' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(newUser);
|
||||
}
|
||||
|
||||
|
@ -78,7 +74,7 @@ export async function deleteTeacherHandler(req: Request, res: Response) {
|
|||
const deletedUser = await deleteTeacher(username);
|
||||
if (!deletedUser) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
error: `User '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -87,58 +83,58 @@ export async function deleteTeacherHandler(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const classes: ClassDTO[] | string[] = full ? await getClassesByTeacher(username) : await getClassIdsByTeacher(username);
|
||||
|
||||
res.status(201).json(classes);
|
||||
} catch (error) {
|
||||
console.error('Error fetching classes by teacher:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const classes = await getClassesByTeacher(username, full);
|
||||
|
||||
if (!classes) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ classes: classes });
|
||||
}
|
||||
|
||||
export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const students: StudentDTO[] | string[] = full ? await getStudentsByTeacher(username) : await getStudentIdsByTeacher(username);
|
||||
|
||||
res.status(201).json(students);
|
||||
} catch (error) {
|
||||
console.error('Error fetching students by teacher:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const students = await getStudentsByTeacher(username, full);
|
||||
|
||||
if (!students) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ students: students });
|
||||
}
|
||||
|
||||
export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.username as string;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const questions: QuestionDTO[] | QuestionId[] = full ? await getQuestionsByTeacher(username) : await getQuestionIdsByTeacher(username);
|
||||
|
||||
res.status(201).json(questions);
|
||||
} catch (error) {
|
||||
console.error('Error fetching questions by teacher:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const questions = await getQuestionsByTeacher(username, full);
|
||||
|
||||
if (!questions) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ questions: questions });
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ interface Translations {
|
|||
};
|
||||
}
|
||||
|
||||
export function getThemes(req: Request, res: Response) {
|
||||
export function getThemesHandler(req: Request, res: Response) {
|
||||
const language = (req.query.language as string)?.toLowerCase() || 'nl';
|
||||
const translations = loadTranslations<Translations>(language);
|
||||
const themeList = themes.map((theme) => ({
|
||||
|
@ -21,8 +21,14 @@ export function getThemes(req: Request, res: Response) {
|
|||
res.json(themeList);
|
||||
}
|
||||
|
||||
export function getThemeByTitle(req: Request, res: Response) {
|
||||
export function getHruidsByThemeHandler(req: Request, res: Response) {
|
||||
const themeKey = req.params.theme;
|
||||
|
||||
if (!themeKey) {
|
||||
res.status(400).json({ error: 'Missing required field: theme' });
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themes.find((t) => t.title === themeKey);
|
||||
|
||||
if (theme) {
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { UserService } from '../services/users.js';
|
||||
import { UserDTO } from '../interfaces/user.js';
|
||||
import { User } from '../entities/users/user.entity.js';
|
||||
|
||||
export async function getAllUsersHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
|
||||
try {
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const users: UserDTO[] | string[] = full ? await service.getAllUsers() : await service.getAllUserIds();
|
||||
|
||||
if (!users) {
|
||||
res.status(404).json({ error: `Users not found.` });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(users);
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching users:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
|
||||
try {
|
||||
const username = req.params.username as string;
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await service.getUserByUsername(username);
|
||||
|
||||
if (!user) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(user);
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching users:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
export async function createUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>, UserClass: new () => T) {
|
||||
try {
|
||||
console.log('req', req);
|
||||
const userData = req.body as UserDTO;
|
||||
|
||||
if (!userData.username || !userData.firstName || !userData.lastName) {
|
||||
res.status(400).json({
|
||||
error: 'Missing required fields: username, firstName, lastName',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newUser = await service.createUser(userData, UserClass);
|
||||
res.status(201).json(newUser);
|
||||
} catch (error) {
|
||||
console.error('❌ Error creating user:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>) {
|
||||
try {
|
||||
const username = req.params.username;
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const deletedUser = await service.deleteUser(username);
|
||||
if (!deletedUser) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json(deletedUser);
|
||||
} catch (error) {
|
||||
console.error('❌ Error deleting user:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@ import { AnyEntity, EntityManager, EntityName, EntityRepository } from '@mikro-o
|
|||
import { forkEntityManager } from '../orm.js';
|
||||
import { StudentRepository } from './users/student-repository.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { User } from '../entities/users/user.entity.js';
|
||||
import { UserRepository } from './users/user-repository.js';
|
||||
import { Teacher } from '../entities/users/teacher.entity.js';
|
||||
import { TeacherRepository } from './users/teacher-repository.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Class } from '../../entities/classes/class.entity.js';
|
||||
import { Student } from '../../entities/users/student.entity.js';
|
||||
import { User } from '../../entities/users/user.entity.js';
|
||||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||
// Import { UserRepository } from './user-repository.js';
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Teacher } from '../../entities/users/teacher.entity.js';
|
||||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||
import { UserRepository } from './user-repository.js';
|
||||
|
||||
export class TeacherRepository extends DwengoEntityRepository<Teacher> {
|
||||
public findByUsername(username: string): Promise<Teacher | null> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Class } from '../classes/class.entity.js';
|
||||
import { Group } from './group.entity.js';
|
||||
import { Language } from '../content/language.js';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Collection, Entity, ManyToMany, ManyToOne, PrimaryKey } from '@mikro-orm/core';
|
||||
import { Entity, ManyToMany, ManyToOne, PrimaryKey } from '@mikro-orm/core';
|
||||
import { Assignment } from './assignment.entity.js';
|
||||
import { Student } from '../users/student.entity.js';
|
||||
import { GroupRepository } from '../../data/assignments/group-repository.js';
|
||||
|
|
|
@ -3,6 +3,12 @@ import { Student } from '../users/student.entity.js';
|
|||
import { Class } from './class.entity.js';
|
||||
import { ClassJoinRequestRepository } from '../../data/classes/class-join-request-repository.js';
|
||||
|
||||
export enum ClassJoinRequestStatus {
|
||||
Open = 'open',
|
||||
Accepted = 'accepted',
|
||||
Declined = 'declined',
|
||||
}
|
||||
|
||||
@Entity({
|
||||
repository: () => ClassJoinRequestRepository,
|
||||
})
|
||||
|
@ -21,10 +27,4 @@ export class ClassJoinRequest {
|
|||
|
||||
@Enum(() => ClassJoinRequestStatus)
|
||||
status!: ClassJoinRequestStatus;
|
||||
}
|
||||
|
||||
export enum ClassJoinRequestStatus {
|
||||
Open = 'open',
|
||||
Accepted = 'accepted',
|
||||
Declined = 'declined',
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { FALLBACK_LANG } from '../config.js';
|
|||
import { Assignment } from '../entities/assignments/assignment.entity.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { languageMap } from '../entities/content/language.js';
|
||||
import { GroupDTO, mapToGroupDTO } from './group.js';
|
||||
import { GroupDTO } from './group.js';
|
||||
|
||||
export interface AssignmentDTO {
|
||||
id: number;
|
||||
|
@ -46,7 +46,5 @@ export function mapToAssignment(assignmentData: AssignmentDTO, cls: Class): Assi
|
|||
assignment.learningPathLanguage = languageMap[assignmentData.language] || FALLBACK_LANG;
|
||||
assignment.within = cls;
|
||||
|
||||
console.log(assignment);
|
||||
|
||||
return assignment;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,6 @@ export interface ClassDTO {
|
|||
teachers: string[];
|
||||
students: string[];
|
||||
joinRequests: string[];
|
||||
endpoints?: {
|
||||
self: string;
|
||||
invitations: string;
|
||||
assignments: string;
|
||||
students: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function mapToClassDTO(cls: Class): ClassDTO {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { Question } from '../entities/questions/question.entity.js';
|
||||
import { UserDTO } from './user.js';
|
||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||
import { mapToStudentDTO, StudentDTO } from './student.js';
|
||||
import { TeacherDTO } from './teacher.js';
|
||||
|
||||
export interface QuestionDTO {
|
||||
learningObjectIdentifier: LearningObjectIdentifier;
|
||||
|
|
|
@ -2,13 +2,10 @@ import { Submission } from '../entities/assignments/submission.entity.js';
|
|||
import { Language } from '../entities/content/language.js';
|
||||
import { GroupDTO, mapToGroupDTO } from './group.js';
|
||||
import { mapToStudent, mapToStudentDTO, StudentDTO } from './student.js';
|
||||
import { mapToUser } from './user';
|
||||
import { Student } from '../entities/users/student.entity';
|
||||
import { LearningObjectIdentifier } from './learning-content.js';
|
||||
|
||||
export interface SubmissionDTO {
|
||||
learningObjectHruid: string;
|
||||
learningObjectLanguage: Language;
|
||||
learningObjectVersion: number;
|
||||
learningObjectIdentifier: LearningObjectIdentifier;
|
||||
|
||||
submissionNumber?: number;
|
||||
submitter: StudentDTO;
|
||||
|
@ -17,11 +14,21 @@ export interface SubmissionDTO {
|
|||
content: string;
|
||||
}
|
||||
|
||||
export interface SubmissionDTOId {
|
||||
learningObjectHruid: string;
|
||||
learningObjectLanguage: Language;
|
||||
learningObjectVersion: number;
|
||||
|
||||
submissionNumber?: number;
|
||||
}
|
||||
|
||||
export function mapToSubmissionDTO(submission: Submission): SubmissionDTO {
|
||||
return {
|
||||
learningObjectHruid: submission.learningObjectHruid,
|
||||
learningObjectLanguage: submission.learningObjectLanguage,
|
||||
learningObjectVersion: submission.learningObjectVersion,
|
||||
learningObjectIdentifier: {
|
||||
hruid: submission.learningObjectHruid,
|
||||
language: submission.learningObjectLanguage,
|
||||
version: submission.learningObjectVersion,
|
||||
},
|
||||
|
||||
submissionNumber: submission.submissionNumber,
|
||||
submitter: mapToStudentDTO(submission.submitter),
|
||||
|
@ -31,11 +38,21 @@ export function mapToSubmissionDTO(submission: Submission): SubmissionDTO {
|
|||
};
|
||||
}
|
||||
|
||||
export function mapToSubmissionDTOId(submission: Submission): SubmissionDTOId {
|
||||
return {
|
||||
learningObjectHruid: submission.learningObjectHruid,
|
||||
learningObjectLanguage: submission.learningObjectLanguage,
|
||||
learningObjectVersion: submission.learningObjectVersion,
|
||||
|
||||
submissionNumber: submission.submissionNumber,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapToSubmission(submissionDTO: SubmissionDTO): Submission {
|
||||
const submission = new Submission();
|
||||
submission.learningObjectHruid = submissionDTO.learningObjectHruid;
|
||||
submission.learningObjectLanguage = submissionDTO.learningObjectLanguage;
|
||||
submission.learningObjectVersion = submissionDTO.learningObjectVersion;
|
||||
submission.learningObjectHruid = submissionDTO.learningObjectIdentifier.hruid;
|
||||
submission.learningObjectLanguage = submissionDTO.learningObjectIdentifier.language;
|
||||
submission.learningObjectVersion = submissionDTO.learningObjectIdentifier.version!;
|
||||
// Submission.submissionNumber = submissionDTO.submissionNumber;
|
||||
submission.submitter = mapToStudent(submissionDTO.submitter);
|
||||
// Submission.submissionTime = submissionDTO.time;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { createLogger, format, Logger as WinstonLogger, transports } from 'winston';
|
||||
import LokiTransport from 'winston-loki';
|
||||
import { LokiLabels } from 'loki-logger-ts';
|
||||
import { LOG_LEVEL, LOKI_HOST } from '../config.js';
|
||||
import { EnvVars, getEnvVar } from '../util/envvars.js';
|
||||
|
||||
export class Logger extends WinstonLogger {
|
||||
constructor() {
|
||||
|
@ -22,10 +22,25 @@ function initializeLogger(): Logger {
|
|||
return logger;
|
||||
}
|
||||
|
||||
const logLevel = getEnvVar(EnvVars.LogLevel);
|
||||
|
||||
const consoleTransport = new transports.Console({
|
||||
level: getEnvVar(EnvVars.LogLevel),
|
||||
format: format.combine(format.cli(), format.colorize()),
|
||||
});
|
||||
|
||||
if (getEnvVar(EnvVars.RunMode) === 'dev') {
|
||||
return createLogger({
|
||||
transports: [consoleTransport],
|
||||
});
|
||||
}
|
||||
|
||||
const lokiHost = getEnvVar(EnvVars.LokiHost);
|
||||
|
||||
const lokiTransport: LokiTransport = new LokiTransport({
|
||||
host: LOKI_HOST,
|
||||
host: lokiHost,
|
||||
labels: Labels,
|
||||
level: LOG_LEVEL,
|
||||
level: logLevel,
|
||||
json: true,
|
||||
format: format.combine(format.timestamp(), format.json()),
|
||||
onConnectionError: (err) => {
|
||||
|
@ -34,16 +49,11 @@ function initializeLogger(): Logger {
|
|||
},
|
||||
});
|
||||
|
||||
const consoleTransport = new transports.Console({
|
||||
level: LOG_LEVEL,
|
||||
format: format.combine(format.cli(), format.colorize()),
|
||||
});
|
||||
|
||||
logger = createLogger({
|
||||
transports: [lokiTransport, consoleTransport],
|
||||
});
|
||||
|
||||
logger.debug(`Logger initialized with level ${LOG_LEVEL}, Loki host ${LOKI_HOST}`);
|
||||
logger.debug(`Logger initialized with level ${logLevel} to Loki host ${lokiHost}`);
|
||||
return logger;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import { PostgreSqlDriver } from '@mikro-orm/postgresql';
|
|||
import { EnvVars, getEnvVar, getNumericEnvVar } from './util/envvars.js';
|
||||
import { SqliteDriver } from '@mikro-orm/sqlite';
|
||||
import { MikroOrmLogger } from './logging/mikroOrmLogger.js';
|
||||
import { LOG_LEVEL } from './config.js';
|
||||
|
||||
// Import alle entity-bestanden handmatig
|
||||
import { User } from './entities/users/user.entity.js';
|
||||
|
@ -69,7 +68,7 @@ function config(testingMode: boolean = false): Options {
|
|||
// EntitiesTs: entitiesTs,
|
||||
|
||||
// Logging
|
||||
debug: LOG_LEVEL === 'debug',
|
||||
debug: getEnvVar(EnvVars.LogLevel) === 'debug',
|
||||
loggerFactory: (options: LoggerOptions) => new MikroOrmLogger(options),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { Response, Router } from 'express';
|
||||
import studentRouter from './students.js';
|
||||
import groupRouter from './groups.js';
|
||||
import assignmentRouter from './assignments.js';
|
||||
import submissionRouter from './submissions.js';
|
||||
import teacherRouter from './teachers.js';
|
||||
import classRouter from './classes.js';
|
||||
import questionRouter from './questions.js';
|
||||
import authRouter from './auth.js';
|
||||
import themeRoutes from './themes.js';
|
||||
import learningPathRoutes from './learning-paths.js';
|
||||
|
@ -22,11 +19,8 @@ router.get('/', (_, res: Response) => {
|
|||
});
|
||||
|
||||
router.use('/student', studentRouter /* #swagger.tags = ['Student'] */);
|
||||
router.use('/group', groupRouter /* #swagger.tags = ['Group'] */);
|
||||
router.use('/assignment', assignmentRouter /* #swagger.tags = ['Assignment'] */);
|
||||
router.use('/submission', submissionRouter /* #swagger.tags = ['Submission'] */);
|
||||
router.use('/teacher', teacherRouter /* #swagger.tags = ['Teacher'] */);
|
||||
router.use('/class', classRouter /* #swagger.tags = ['Class'] */);
|
||||
router.use('/question', questionRouter /* #swagger.tags = ['Question'] */);
|
||||
router.use('/auth', authRouter /* #swagger.tags = ['Auth'] */);
|
||||
router.use('/theme', themeRoutes /* #swagger.tags = ['Theme'] */);
|
||||
router.use('/learningPath', learningPathRoutes /* #swagger.tags = ['Learning Path'] */);
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
getStudentHandler,
|
||||
getStudentSubmissionsHandler,
|
||||
} from '../controllers/students.js';
|
||||
import { getStudentGroups } from '../services/students.js';
|
||||
const router = express.Router();
|
||||
|
||||
// Root endpoint used to search objects
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import express from 'express';
|
||||
import { getThemes, getThemeByTitle } from '../controllers/themes.js';
|
||||
import { getThemesHandler, getHruidsByThemeHandler } from '../controllers/themes.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Query: language
|
||||
// Route to fetch list of {key, title, description, image} themes in their respective language
|
||||
router.get('/', getThemes);
|
||||
router.get('/', getThemesHandler);
|
||||
|
||||
// Arg: theme (key)
|
||||
// Route to fetch list of hruids based on theme
|
||||
router.get('/:theme', getThemeByTitle);
|
||||
router.get('/:theme', getHruidsByThemeHandler);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { getAssignmentRepository, getClassRepository, getGroupRepository, getSubmissionRepository } from '../data/repositories.js';
|
||||
import { Assignment } from '../entities/assignments/assignment.entity.js';
|
||||
import { AssignmentDTO, mapToAssignment, mapToAssignmentDTO, mapToAssignmentDTOId } from '../interfaces/assignment.js';
|
||||
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
|
||||
|
||||
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> {
|
||||
const classRepository = getClassRepository();
|
||||
|
@ -21,7 +20,7 @@ export async function getAllAssignments(classid: string, full: boolean): Promise
|
|||
return assignments.map(mapToAssignmentDTOId);
|
||||
}
|
||||
|
||||
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<Assignment | null> {
|
||||
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<AssignmentDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
|
@ -36,8 +35,9 @@ export async function createAssignment(classid: string, assignmentData: Assignme
|
|||
const newAssignment = assignmentRepository.create(assignment);
|
||||
await assignmentRepository.save(newAssignment);
|
||||
|
||||
return newAssignment;
|
||||
return mapToAssignmentDTO(newAssignment);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,11 @@ export async function getAssignment(classid: string, id: number): Promise<Assign
|
|||
return mapToAssignmentDTO(assignment);
|
||||
}
|
||||
|
||||
export async function getAssignmentsSubmissions(classid: string, assignmentNumber: number): Promise<SubmissionDTO[]> {
|
||||
export async function getAssignmentsSubmissions(
|
||||
classid: string,
|
||||
assignmentNumber: number,
|
||||
full: boolean
|
||||
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
|
@ -81,5 +85,9 @@ export async function getAssignmentsSubmissions(classid: string, assignmentNumbe
|
|||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = (await Promise.all(groups.map((group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
|
||||
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
if (full) {
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
}
|
||||
|
||||
return submissions.map(mapToSubmissionDTOId);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { getClassRepository, getStudentRepository, getTeacherInvitationRepository, getTeacherRepository } from '../data/repositories.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
|
||||
import { mapToStudentDTO, StudentDTO } from '../interfaces/student.js';
|
||||
import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds, TeacherInvitationDTO } from '../interfaces/teacher-invitation.js';
|
||||
|
@ -22,16 +21,14 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
|
|||
return classes.map((cls) => cls.classId!);
|
||||
}
|
||||
|
||||
export async function createClass(classData: ClassDTO): Promise<Class | null> {
|
||||
export async function createClass(classData: ClassDTO): Promise<ClassDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacherUsernames = classData.teachers || [];
|
||||
const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher != null);
|
||||
const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher !== null);
|
||||
|
||||
const studentRepository = getStudentRepository();
|
||||
const studentUsernames = classData.students || [];
|
||||
const students = (await Promise.all(studentUsernames.map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null);
|
||||
|
||||
//Const cls = mapToClass(classData, teachers, students);
|
||||
const students = (await Promise.all(studentUsernames.map((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
|
||||
|
@ -43,7 +40,7 @@ export async function createClass(classData: ClassDTO): Promise<Class | null> {
|
|||
});
|
||||
await classRepository.save(newClass);
|
||||
|
||||
return newClass;
|
||||
return mapToClassDTO(newClass);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return null;
|
|
@ -1,4 +1,3 @@
|
|||
import { GroupRepository } from '../data/assignments/group-repository.js';
|
||||
import {
|
||||
getAssignmentRepository,
|
||||
getClassRepository,
|
||||
|
@ -8,7 +7,7 @@ import {
|
|||
} from '../data/repositories.js';
|
||||
import { Group } from '../entities/assignments/group.entity.js';
|
||||
import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
|
||||
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
|
||||
|
||||
export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number, full: boolean): Promise<GroupDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
|
@ -43,7 +42,7 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme
|
|||
const studentRepository = getStudentRepository();
|
||||
|
||||
const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list
|
||||
const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null);
|
||||
const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null);
|
||||
|
||||
console.log(members);
|
||||
|
||||
|
@ -103,7 +102,12 @@ export async function getAllGroups(classId: string, assignmentNumber: number, fu
|
|||
return groups.map(mapToGroupDTOId);
|
||||
}
|
||||
|
||||
export async function getGroupSubmissions(classId: string, assignmentNumber: number, groupNumber: number): Promise<SubmissionDTO[]> {
|
||||
export async function getGroupSubmissions(
|
||||
classId: string,
|
||||
assignmentNumber: number,
|
||||
groupNumber: number,
|
||||
full: boolean
|
||||
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
|
@ -128,5 +132,9 @@ export async function getGroupSubmissions(classId: string, assignmentNumber: num
|
|||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = await submissionRepository.findAllSubmissionsForGroup(group);
|
||||
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
if (full) {
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
}
|
||||
|
||||
return submissions.map(mapToSubmissionDTOId);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,13 @@ export async function getLearningObjectById(hruid: string, language: string): Pr
|
|||
return filterData(metadata, htmlUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to fetch learning paths
|
||||
*/
|
||||
function fetchLearningPaths(arg0: string[], language: string, arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to fetch learning objects (full data or just HRUIDs)
|
||||
*/
|
||||
|
@ -85,6 +92,4 @@ export async function getLearningObjectsFromPath(hruid: string, language: string
|
|||
export async function getLearningObjectIdsFromPath(hruid: string, language: string): Promise<string[]> {
|
||||
return (await fetchLearningObjects(hruid, false, language)) as string[];
|
||||
}
|
||||
function fetchLearningPaths(arg0: string[], language: string, arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
|
|
|
@ -103,5 +103,5 @@ export async function deleteQuestion(questionId: QuestionId) {
|
|||
return null;
|
||||
}
|
||||
|
||||
return question;
|
||||
return mapToQuestionDTO(question);
|
||||
}
|
||||
|
|
|
@ -5,19 +5,18 @@ import { AssignmentDTO } from '../interfaces/assignment.js';
|
|||
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
|
||||
import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
|
||||
import { mapToStudent, mapToStudentDTO, StudentDTO } from '../interfaces/student.js';
|
||||
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
|
||||
import { getAllAssignments } from './assignments.js';
|
||||
import { UserService } from './users.js';
|
||||
|
||||
export async function getAllStudents(): Promise<StudentDTO[]> {
|
||||
export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const users = await studentRepository.findAll();
|
||||
return users.map(mapToStudentDTO);
|
||||
}
|
||||
const students = await studentRepository.findAll();
|
||||
|
||||
export async function getAllStudentIds(): Promise<string[]> {
|
||||
const users = await getAllStudents();
|
||||
return users.map((user) => user.username);
|
||||
if (full) {
|
||||
return students.map(mapToStudentDTO);
|
||||
}
|
||||
|
||||
return students.map((student) => student.username);
|
||||
}
|
||||
|
||||
export async function getStudent(username: string): Promise<StudentDTO | null> {
|
||||
|
@ -111,7 +110,7 @@ export async function getStudentGroups(username: string, full: boolean): Promise
|
|||
return groups.map(mapToGroupDTOId);
|
||||
}
|
||||
|
||||
export async function getStudentSubmissions(username: string): Promise<SubmissionDTO[]> {
|
||||
export async function getStudentSubmissions(username: string, full: boolean): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const student = await studentRepository.findByUsername(username);
|
||||
|
||||
|
@ -122,5 +121,9 @@ export async function getStudentSubmissions(username: string): Promise<Submissio
|
|||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = await submissionRepository.findAllSubmissionsForStudent(student);
|
||||
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
if (full) {
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
}
|
||||
|
||||
return submissions.map(mapToSubmissionDTOId);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ export async function createSubmission(submissionDTO: SubmissionDTO) {
|
|||
return null;
|
||||
}
|
||||
|
||||
return submission;
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
export async function deleteSubmission(learningObjectHruid: string, language: Language, version: number, submissionNumber: number) {
|
||||
|
|
|
@ -7,22 +7,22 @@ import {
|
|||
} from '../data/repositories.js';
|
||||
import { Teacher } from '../entities/users/teacher.entity.js';
|
||||
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
|
||||
import { getClassStudents } from './class.js';
|
||||
import { getClassStudents } from './classes.js';
|
||||
import { StudentDTO } from '../interfaces/student.js';
|
||||
import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js';
|
||||
import { UserService } from './users.js';
|
||||
import { mapToUser } from '../interfaces/user.js';
|
||||
import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js';
|
||||
import { teachersOnly } from '../middleware/auth/auth.js';
|
||||
|
||||
export async function getAllTeachers(): Promise<TeacherDTO[]> {
|
||||
export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const users = await teacherRepository.findAll();
|
||||
return users.map(mapToTeacherDTO);
|
||||
}
|
||||
const teachers = await teacherRepository.findAll();
|
||||
|
||||
export async function getAllTeacherIds(): Promise<string[]> {
|
||||
const users = await getAllTeachers();
|
||||
return users.map((user) => user.username);
|
||||
if (full) {
|
||||
return teachers.map(mapToTeacherDTO);
|
||||
}
|
||||
|
||||
return teachers.map((teacher) => teacher.username);
|
||||
}
|
||||
|
||||
export async function getTeacher(username: string): Promise<TeacherDTO | null> {
|
||||
|
@ -64,11 +64,11 @@ export async function deleteTeacher(username: string): Promise<TeacherDTO | null
|
|||
}
|
||||
}
|
||||
|
||||
export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[]> {
|
||||
export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[] | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacher = await teacherRepository.findByUsername(username);
|
||||
if (!teacher) {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
|
@ -76,35 +76,49 @@ export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[
|
|||
return classes.map(mapToClassDTO);
|
||||
}
|
||||
|
||||
export async function getClassesByTeacher(username: string): Promise<ClassDTO[]> {
|
||||
return await fetchClassesByTeacher(username);
|
||||
}
|
||||
|
||||
export async function getClassIdsByTeacher(username: string): Promise<string[]> {
|
||||
export async function getClassesByTeacher(username: string, full: boolean): Promise<ClassDTO[] | string[] | null> {
|
||||
const classes = await fetchClassesByTeacher(username);
|
||||
|
||||
if (!classes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (full) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
return classes.map((cls) => cls.id);
|
||||
}
|
||||
|
||||
export async function fetchStudentsByTeacher(username: string) {
|
||||
const classes = await getClassIdsByTeacher(username);
|
||||
export async function fetchStudentsByTeacher(username: string): Promise<StudentDTO[] | null> {
|
||||
const classes = (await getClassesByTeacher(username, false)) as string[];
|
||||
|
||||
if (!classes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat();
|
||||
}
|
||||
|
||||
export async function getStudentsByTeacher(username: string): Promise<StudentDTO[]> {
|
||||
return await fetchStudentsByTeacher(username);
|
||||
}
|
||||
|
||||
export async function getStudentIdsByTeacher(username: string): Promise<string[]> {
|
||||
export async function getStudentsByTeacher(username: string, full: boolean): Promise<StudentDTO[] | string[] | null> {
|
||||
const students = await fetchStudentsByTeacher(username);
|
||||
|
||||
if (!students) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (full) {
|
||||
return students;
|
||||
}
|
||||
|
||||
return students.map((student) => student.username);
|
||||
}
|
||||
|
||||
export async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[]> {
|
||||
export async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[] | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacher = await teacherRepository.findByUsername(username);
|
||||
if (!teacher) {
|
||||
throw new Error(`Teacher with username '${username}' not found.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find all learning objects that this teacher manages
|
||||
|
@ -118,12 +132,16 @@ export async function fetchTeacherQuestions(username: string): Promise<QuestionD
|
|||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
export async function getQuestionsByTeacher(username: string): Promise<QuestionDTO[]> {
|
||||
return await fetchTeacherQuestions(username);
|
||||
}
|
||||
|
||||
export async function getQuestionIdsByTeacher(username: string): Promise<QuestionId[]> {
|
||||
export async function getQuestionsByTeacher(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[] | null> {
|
||||
const questions = await fetchTeacherQuestions(username);
|
||||
|
||||
if (!questions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (full) {
|
||||
return questions;
|
||||
}
|
||||
|
||||
return questions.map(mapToQuestionId);
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { UserRepository } from '../data/users/user-repository.js';
|
||||
import { UserDTO, mapToUser, mapToUserDTO } from '../interfaces/user.js';
|
||||
import { User } from '../entities/users/user.entity.js';
|
||||
|
||||
export class UserService<T extends User> {
|
||||
protected repository: UserRepository<T>;
|
||||
|
||||
constructor(repository: UserRepository<T>) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
async getAllUsers(): Promise<UserDTO[]> {
|
||||
const users = await this.repository.findAll();
|
||||
return users.map(mapToUserDTO);
|
||||
}
|
||||
|
||||
async getAllUserIds(): Promise<string[]> {
|
||||
const users = await this.getAllUsers();
|
||||
return users.map((user) => user.username);
|
||||
}
|
||||
|
||||
async getUserByUsername(username: string): Promise<UserDTO | null> {
|
||||
const user = await this.repository.findByUsername(username);
|
||||
return user ? mapToUserDTO(user) : null;
|
||||
}
|
||||
|
||||
async createUser(userData: UserDTO, UserClass: new () => T): Promise<T> {
|
||||
const newUser = mapToUser(userData, new UserClass());
|
||||
await this.repository.save(newUser);
|
||||
return newUser;
|
||||
}
|
||||
|
||||
async deleteUser(username: string): Promise<UserDTO | null> {
|
||||
const user = await this.getUserByUsername(username);
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
await this.repository.deleteByUsername(username);
|
||||
return mapToUserDTO(user);
|
||||
}
|
||||
}
|
|
@ -4,20 +4,24 @@ const IDP_PREFIX = PREFIX + 'AUTH_';
|
|||
const STUDENT_IDP_PREFIX = IDP_PREFIX + 'STUDENT_';
|
||||
const TEACHER_IDP_PREFIX = IDP_PREFIX + 'TEACHER_';
|
||||
const CORS_PREFIX = PREFIX + 'CORS_';
|
||||
const LOGGING_PREFIX = PREFIX + 'LOGGING_';
|
||||
|
||||
type EnvVar = { key: string; required?: boolean; defaultValue?: any };
|
||||
|
||||
export const EnvVars: { [key: string]: EnvVar } = {
|
||||
Port: { key: PREFIX + 'PORT', defaultValue: 3000 },
|
||||
LearningContentRepoApiBaseUrl: { key: PREFIX + 'LEARNING_CONTENT_REPO_API_BASE_URL', defaultValue: 'https://dwengo.org/backend/api' },
|
||||
FallbackLanguage: { key: PREFIX + 'FALLBACK_LANGUAGE', defaultValue: 'nl' },
|
||||
RunMode: { key: PREFIX + 'RUN_MODE', defaultValue: 'dev' },
|
||||
|
||||
DbHost: { key: DB_PREFIX + 'HOST', required: true },
|
||||
DbPort: { key: DB_PREFIX + 'PORT', defaultValue: 5432 },
|
||||
DbName: { key: DB_PREFIX + 'NAME', defaultValue: 'dwengo' },
|
||||
DbUsername: { key: DB_PREFIX + 'USERNAME', required: true },
|
||||
DbPassword: { key: DB_PREFIX + 'PASSWORD', required: true },
|
||||
DbUpdate: { key: DB_PREFIX + 'UPDATE', defaultValue: false },
|
||||
LearningContentRepoApiBaseUrl: { key: PREFIX + 'LEARNING_CONTENT_REPO_API_BASE_URL', defaultValue: 'https://dwengo.org/backend/api' },
|
||||
FallbackLanguage: { key: PREFIX + 'FALLBACK_LANGUAGE', defaultValue: 'nl' },
|
||||
UserContentPrefix: { key: DB_PREFIX + 'USER_CONTENT_PREFIX', defaultValue: 'u_' },
|
||||
|
||||
IdpStudentUrl: { key: STUDENT_IDP_PREFIX + 'URL', required: true },
|
||||
IdpStudentClientId: { key: STUDENT_IDP_PREFIX + 'CLIENT_ID', required: true },
|
||||
IdpStudentJwksEndpoint: { key: STUDENT_IDP_PREFIX + 'JWKS_ENDPOINT', required: true },
|
||||
|
@ -25,8 +29,12 @@ export const EnvVars: { [key: string]: EnvVar } = {
|
|||
IdpTeacherClientId: { key: TEACHER_IDP_PREFIX + 'CLIENT_ID', required: true },
|
||||
IdpTeacherJwksEndpoint: { key: TEACHER_IDP_PREFIX + 'JWKS_ENDPOINT', required: true },
|
||||
IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' },
|
||||
|
||||
CorsAllowedOrigins: { key: CORS_PREFIX + 'ALLOWED_ORIGINS', defaultValue: '' },
|
||||
CorsAllowedHeaders: { key: CORS_PREFIX + 'ALLOWED_HEADERS', defaultValue: 'Authorization,Content-Type' },
|
||||
|
||||
LogLevel: { key: LOGGING_PREFIX + 'LEVEL', defaultValue: 'info' },
|
||||
LokiHost: { key: LOGGING_PREFIX + 'LOKI_HOST', defaultValue: 'http://localhost:3102' },
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,12 +8,12 @@ const logger: Logger = getLogger();
|
|||
|
||||
export function loadTranslations<T>(language: string): T {
|
||||
try {
|
||||
const filePath = path.join(process.cwd(), '_i18n', `${language}.yml`);
|
||||
const filePath = path.join(process.cwd(), 'i18n', `${language}.yml`);
|
||||
const yamlFile = fs.readFileSync(filePath, 'utf8');
|
||||
return yaml.load(yamlFile) as T;
|
||||
} catch (error) {
|
||||
logger.warn(`Cannot load translation for ${language}, fallen back to dutch`, error);
|
||||
const fallbackPath = path.join(process.cwd(), '_i18n', `${FALLBACK_LANG}.yml`);
|
||||
const fallbackPath = path.join(process.cwd(), 'i18n', `${FALLBACK_LANG}.yml`);
|
||||
return yaml.load(fs.readFileSync(fallbackPath, 'utf8')) as T;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue