mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
trying to integrate TS
This commit is contained in:
parent
4cca066751
commit
55fe6003c6
36 changed files with 1979 additions and 1382 deletions
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug backend",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/server/app.js",
|
||||||
|
"cwd":"${workspaceFolder}/server/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "msedge",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug frontend",
|
||||||
|
"url": "http://localhost:5173",
|
||||||
|
"webRoot": "${workspaceFolder}/client/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "EvalueTonSavoir",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
1
server/.gitignore
vendored
Normal file
1
server/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
auth_config.json
|
||||||
100
server/app.js
100
server/app.js
|
|
@ -1,100 +0,0 @@
|
||||||
// Import API
|
|
||||||
const express = require("express");
|
|
||||||
const http = require("http");
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
|
|
||||||
// Import Sockets
|
|
||||||
const { setupWebsocket } = require("./socket/socket");
|
|
||||||
const { Server } = require("socket.io");
|
|
||||||
|
|
||||||
// instantiate the db
|
|
||||||
const db = require('./config/db.js');
|
|
||||||
// instantiate the models
|
|
||||||
const quiz = require('./models/quiz.js');
|
|
||||||
const quizModel = new quiz(db);
|
|
||||||
const folders = require('./models/folders.js');
|
|
||||||
const foldersModel = new folders(db, quizModel);
|
|
||||||
const users = require('./models/users.js');
|
|
||||||
const userModel = new users(db, foldersModel);
|
|
||||||
const images = require('./models/images.js');
|
|
||||||
const imageModel = new images(db);
|
|
||||||
|
|
||||||
// instantiate the controllers
|
|
||||||
const usersController = require('./controllers/users.js');
|
|
||||||
const usersControllerInstance = new usersController(userModel);
|
|
||||||
const foldersController = require('./controllers/folders.js');
|
|
||||||
const foldersControllerInstance = new foldersController(foldersModel);
|
|
||||||
const quizController = require('./controllers/quiz.js');
|
|
||||||
const quizControllerInstance = new quizController(quizModel, foldersModel);
|
|
||||||
const imagesController = require('./controllers/images.js');
|
|
||||||
const imagesControllerInstance = new imagesController(imageModel);
|
|
||||||
|
|
||||||
// export the controllers
|
|
||||||
module.exports.users = usersControllerInstance;
|
|
||||||
module.exports.folders = foldersControllerInstance;
|
|
||||||
module.exports.quizzes = quizControllerInstance;
|
|
||||||
module.exports.images = imagesControllerInstance;
|
|
||||||
|
|
||||||
//import routers (instantiate controllers as side effect)
|
|
||||||
const userRouter = require('./routers/users.js');
|
|
||||||
const folderRouter = require('./routers/folders.js');
|
|
||||||
const quizRouter = require('./routers/quiz.js');
|
|
||||||
const imagesRouter = require('./routers/images.js');
|
|
||||||
|
|
||||||
// Setup environment
|
|
||||||
dotenv.config();
|
|
||||||
const errorHandler = require("./middleware/errorHandler.js");
|
|
||||||
|
|
||||||
// Start app
|
|
||||||
const app = express();
|
|
||||||
const cors = require("cors");
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
|
|
||||||
const configureServer = (httpServer, isDev) => {
|
|
||||||
return new Server(httpServer, {
|
|
||||||
path: "/socket.io",
|
|
||||||
cors: {
|
|
||||||
origin: "*",
|
|
||||||
methods: ["GET", "POST"],
|
|
||||||
credentials: true,
|
|
||||||
},
|
|
||||||
secure: !isDev, // true for https, false for http
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start sockets (depending on the dev or prod environment)
|
|
||||||
let server = http.createServer(app);
|
|
||||||
let isDev = process.env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
console.log(`Environnement: ${process.env.NODE_ENV} (${isDev ? 'dev' : 'prod'})`);
|
|
||||||
|
|
||||||
const io = configureServer(server);
|
|
||||||
|
|
||||||
setupWebsocket(io);
|
|
||||||
app.use(cors());
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
|
|
||||||
// Create routes
|
|
||||||
app.use('/api/user', userRouter);
|
|
||||||
app.use('/api/folder', folderRouter);
|
|
||||||
app.use('/api/quiz', quizRouter);
|
|
||||||
app.use('/api/image', imagesRouter);
|
|
||||||
|
|
||||||
app.use(errorHandler);
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
async function start() {
|
|
||||||
const port = process.env.PORT || 4400;
|
|
||||||
|
|
||||||
// Check DB connection
|
|
||||||
await db.connect();
|
|
||||||
db.getConnection();
|
|
||||||
console.log(`Connexion MongoDB établie`);
|
|
||||||
|
|
||||||
server.listen(port, () => {
|
|
||||||
console.log(`Serveur écoutant sur le port ${port}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
136
server/app.ts
Normal file
136
server/app.ts
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
import express, { Application } from 'express';
|
||||||
|
import http from 'http';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import cors from 'cors';
|
||||||
|
import bodyParser from 'body-parser';
|
||||||
|
import { ServerOptions, Server as SocketIOServer } from 'socket.io';
|
||||||
|
import { GlideClient } from '@valkey/valkey-glide';
|
||||||
|
|
||||||
|
// Set app defaults
|
||||||
|
const environment: string = process.env.NODE_ENV ?? "production";
|
||||||
|
const isDev: boolean = environment === "development";
|
||||||
|
|
||||||
|
// Import Sockets
|
||||||
|
import setupWebsocket from "./socket/socket";
|
||||||
|
|
||||||
|
// Import Database
|
||||||
|
import db from './config/db';
|
||||||
|
|
||||||
|
// Import Models
|
||||||
|
import Quiz from './models/quiz';
|
||||||
|
import Folders from './models/folders';
|
||||||
|
import Users from './models/users';
|
||||||
|
import Images from './models/images';
|
||||||
|
|
||||||
|
// Instantiate models
|
||||||
|
const quizModel = new Quiz(db);
|
||||||
|
const foldersModel = new Folders(db, quizModel);
|
||||||
|
const userModel = new Users(db, foldersModel);
|
||||||
|
const imageModel = new Images(db);
|
||||||
|
|
||||||
|
// Initialize cache
|
||||||
|
const valkey = await GlideClient.createClient({
|
||||||
|
addresses: [{
|
||||||
|
host: process.env.VALKEY_HOST ?? 'localhost',
|
||||||
|
port: Number(process.env.VALKEY_PORT) ?? 6379
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Import Controllers
|
||||||
|
import UsersController from './controllers/users';
|
||||||
|
import FoldersController from './controllers/folders';
|
||||||
|
import QuizController from './controllers/quiz';
|
||||||
|
import ImagesController from './controllers/images';
|
||||||
|
import { RoomManager as RoomsController } from './controllers/rooms';
|
||||||
|
|
||||||
|
// Instantiate Controllers
|
||||||
|
const usersControllerInstance = new UsersController(userModel);
|
||||||
|
const foldersControllerInstance = new FoldersController(foldersModel);
|
||||||
|
const quizControllerInstance = new QuizController(quizModel, foldersModel);
|
||||||
|
const imagesControllerInstance = new ImagesController(imageModel);
|
||||||
|
|
||||||
|
// Initialize valkey before creating rooms controller
|
||||||
|
const roomsControllerInstance = new RoomsController({}, valkey);
|
||||||
|
|
||||||
|
|
||||||
|
// Export Controllers
|
||||||
|
export const controllers = {
|
||||||
|
users: usersControllerInstance,
|
||||||
|
folders: foldersControllerInstance,
|
||||||
|
quizzes: quizControllerInstance,
|
||||||
|
images: imagesControllerInstance,
|
||||||
|
rooms: roomsControllerInstance
|
||||||
|
};
|
||||||
|
|
||||||
|
// Import Routers
|
||||||
|
import userRouter from './routers/users';
|
||||||
|
import folderRouter from './routers/folders';
|
||||||
|
import quizRouter from './routers/quiz';
|
||||||
|
import imagesRouter from './routers/images';
|
||||||
|
|
||||||
|
// Setup environment
|
||||||
|
dotenv.config();
|
||||||
|
import errorHandler from "./middleware/errorHandler";
|
||||||
|
|
||||||
|
// Start app
|
||||||
|
const app: Application = express();
|
||||||
|
|
||||||
|
const configureServer = (httpServer: http.Server, isDev: boolean): SocketIOServer => {
|
||||||
|
|
||||||
|
isDev
|
||||||
|
const options: Partial<ServerOptions> = {
|
||||||
|
path: "/socket.io",
|
||||||
|
cors: {
|
||||||
|
origin: "*", // Not secure -- to
|
||||||
|
methods: ["GET", "POST"],
|
||||||
|
credentials: true,
|
||||||
|
},
|
||||||
|
transports: ['websocket', 'polling'],
|
||||||
|
};
|
||||||
|
|
||||||
|
return new SocketIOServer(httpServer, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create HTTP server
|
||||||
|
const server: http.Server = http.createServer(app);
|
||||||
|
|
||||||
|
console.log(`Environment: ${process.env.NODE_ENV} (${isDev ? 'dev' : 'prod'})`);
|
||||||
|
|
||||||
|
const io: SocketIOServer = configureServer(server, isDev);
|
||||||
|
|
||||||
|
setupWebsocket(io);
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.use('/api/user', userRouter);
|
||||||
|
app.use('/api/folder', folderRouter);
|
||||||
|
app.use('/api/quiz', quizRouter);
|
||||||
|
app.use('/api/image', imagesRouter);
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
app.use(errorHandler);
|
||||||
|
|
||||||
|
// Server startup function
|
||||||
|
const start = async (): Promise<void> => {
|
||||||
|
const port: number = Number(process.env.PORT) || 4400;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check DB connection
|
||||||
|
await db.connect();
|
||||||
|
db.getConnection();
|
||||||
|
console.log('MongoDB connection established');
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`Server listening on port ${port}`);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to start server:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
const { MongoClient } = require('mongodb');
|
|
||||||
const dotenv = require('dotenv')
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
class DBConnection {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.mongoURI = process.env.MONGO_URI;
|
|
||||||
this.databaseName = process.env.MONGO_DATABASE;
|
|
||||||
this.connection = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async connect() {
|
|
||||||
const client = new MongoClient(this.mongoURI);
|
|
||||||
this.connection = await client.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
getConnection() {
|
|
||||||
if (!this.connection) {
|
|
||||||
throw new Error('Connexion MongoDB non établie');
|
|
||||||
}
|
|
||||||
return this.connection.db(this.databaseName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = new DBConnection();
|
|
||||||
module.exports = instance;
|
|
||||||
25
server/config/db.ts
Normal file
25
server/config/db.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { MongoClient } from 'mongodb';
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export class DBConnection {
|
||||||
|
|
||||||
|
mongoURI:string = process.env.MONGO_URI ?? "localhost:27017"
|
||||||
|
databaseName:string = process.env.MONGO_DATABASE ?? "mangodb"
|
||||||
|
connection:MongoClient | undefined = undefined;
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
const client = new MongoClient(this.mongoURI!);
|
||||||
|
this.connection = await client.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
getConnection() {
|
||||||
|
if (!this.connection) {
|
||||||
|
throw new Error('Connexion MongoDB non établie');
|
||||||
|
}
|
||||||
|
return this.connection.db(this.databaseName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DBConnection();
|
||||||
|
|
@ -1,21 +1,18 @@
|
||||||
const nodemailer = require('nodemailer');
|
import nodemailer from 'nodemailer';
|
||||||
const dotenv = require('dotenv');
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
class Emailer {
|
class Emailer {
|
||||||
|
senderEmail = process.env.SENDER_EMAIL;
|
||||||
constructor() {
|
psw = process.env.EMAIL_PSW;
|
||||||
this.senderEmail = process.env.SENDER_EMAIL;
|
transporter = nodemailer.createTransport({
|
||||||
this.psw = process.env.EMAIL_PSW;
|
service: process.env.EMAIL_SERVICE,
|
||||||
this.transporter = nodemailer.createTransport({
|
auth: {
|
||||||
service: process.env.EMAIL_SERVICE,
|
user: this.senderEmail,
|
||||||
auth: {
|
pass: this.psw
|
||||||
user: this.senderEmail,
|
}
|
||||||
pass: this.psw
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
registerConfirmation(email) {
|
registerConfirmation(email) {
|
||||||
this.transporter.sendMail({
|
this.transporter.sendMail({
|
||||||
|
|
@ -46,4 +43,4 @@ class Emailer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Emailer();
|
export default new Emailer()
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
exports.UNAUTHORIZED_NO_TOKEN_GIVEN = {
|
|
||||||
message: 'Accès refusé. Aucun jeton fourni.',
|
|
||||||
code: 401
|
|
||||||
}
|
|
||||||
exports.UNAUTHORIZED_INVALID_TOKEN = {
|
|
||||||
message: 'Accès refusé. Jeton invalide.',
|
|
||||||
code: 401
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.MISSING_REQUIRED_PARAMETER = {
|
|
||||||
message: 'Paramètre requis manquant.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.USER_ALREADY_EXISTS = {
|
|
||||||
message: 'L\'utilisateur existe déjà.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.LOGIN_CREDENTIALS_ERROR = {
|
|
||||||
message: 'L\'email et le mot de passe ne correspondent pas.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.GENERATE_PASSWORD_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la création d\'un nouveau mot de passe.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.UPDATE_PASSWORD_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la mise à jours du mot de passe.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.DELETE_USER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de supression de l\'utilisateur.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.IMAGE_NOT_FOUND = {
|
|
||||||
message: 'Nous n\'avons pas trouvé l\'image.',
|
|
||||||
code: 404
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.QUIZ_NOT_FOUND = {
|
|
||||||
message: 'Aucun quiz portant cet identifiant n\'a été trouvé.',
|
|
||||||
code: 404
|
|
||||||
}
|
|
||||||
exports.QUIZ_ALREADY_EXISTS = {
|
|
||||||
message: 'Le quiz existe déja.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.UPDATE_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la mise à jours du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.DELETE_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la supression du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.GETTING_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la récupération du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.MOVING_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors du déplacement du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.DUPLICATE_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la duplication du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.COPY_QUIZ_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la copie du quiz.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.FOLDER_NOT_FOUND = {
|
|
||||||
message: 'Aucun dossier portant cet identifiant n\'a été trouvé.',
|
|
||||||
code: 404
|
|
||||||
}
|
|
||||||
exports.FOLDER_ALREADY_EXISTS = {
|
|
||||||
message: 'Le dossier existe déja.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.UPDATE_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la mise à jours du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.DELETE_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la supression du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.GETTING_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la récupération du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.MOVING_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors du déplacement du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.DUPLICATE_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la duplication du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
exports.COPY_FOLDER_ERROR = {
|
|
||||||
message: 'Une erreur s\'est produite lors de la copie du dossier.',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.NOT_IMPLEMENTED = {
|
|
||||||
message: 'Route not implemented yet!',
|
|
||||||
code: 400
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static ok(res, results) {200
|
|
||||||
// static badRequest(res, message) {400
|
|
||||||
// static unauthorized(res, message) {401
|
|
||||||
// static notFound(res, message) {404
|
|
||||||
// static serverError(res, message) {505
|
|
||||||
129
server/constants/errorCodes.ts
Normal file
129
server/constants/errorCodes.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import type { AppErrorInfos } from "../middleware/AppError";
|
||||||
|
import { HttpStatusCode } from "../utils/http-status-codes";
|
||||||
|
|
||||||
|
export const UNAUTHORIZED_NO_TOKEN_GIVEN:AppErrorInfos = {
|
||||||
|
message: 'Accès refusé. Aucun jeton fourni.',
|
||||||
|
statusCode: HttpStatusCode.UNAUTHORIZED
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UNAUTHORIZED_INVALID_TOKEN:AppErrorInfos = {
|
||||||
|
message: 'Accès refusé. Jeton invalide.',
|
||||||
|
statusCode: HttpStatusCode.UNAUTHORIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MISSING_REQUIRED_PARAMETER:AppErrorInfos = {
|
||||||
|
message: 'Paramètre requis manquant.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
export const USER_ALREADY_EXISTS:AppErrorInfos = {
|
||||||
|
message: 'L\'utilisateur existe déjà.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const USER_NOT_FOUND:AppErrorInfos = {
|
||||||
|
message: 'L\'utilisateur n\'existe pas.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const USER_CONTROLLER_NOT_INITIALIZED:AppErrorInfos = {
|
||||||
|
message: "Le controlleur d'utilisateur n'est pas initialisé",
|
||||||
|
statusCode: HttpStatusCode.INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
export const LOGIN_CREDENTIALS_ERROR:AppErrorInfos = {
|
||||||
|
message: 'L\'email et le mot de passe ne correspondent pas.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const GENERATE_PASSWORD_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la création d\'un nouveau mot de passe.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const UPDATE_PASSWORD_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la mise à jours du mot de passe.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const DELETE_USER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de supression de l\'utilisateur.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IMAGE_NOT_FOUND:AppErrorInfos = {
|
||||||
|
message: 'Nous n\'avons pas trouvé l\'image.',
|
||||||
|
statusCode: HttpStatusCode.NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QUIZ_NOT_FOUND:AppErrorInfos = {
|
||||||
|
message: 'Aucun quiz portant cet identifiant n\'a été trouvé.',
|
||||||
|
statusCode: HttpStatusCode.NOT_FOUND
|
||||||
|
}
|
||||||
|
export const QUIZ_ALREADY_EXISTS:AppErrorInfos = {
|
||||||
|
message: 'Le quiz existe déja.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const UPDATE_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la mise à jours du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const DELETE_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la supression du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const GETTING_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la récupération du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const MOVING_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors du déplacement du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const DUPLICATE_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la duplication du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const COPY_QUIZ_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la copie du quiz.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FOLDER_NOT_FOUND:AppErrorInfos = {
|
||||||
|
message: 'Aucun dossier portant cet identifiant n\'a été trouvé.',
|
||||||
|
statusCode: HttpStatusCode.NOT_FOUND
|
||||||
|
}
|
||||||
|
export const FOLDER_ALREADY_EXISTS:AppErrorInfos = {
|
||||||
|
message: 'Le dossier existe déja.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const UPDATE_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la mise à jours du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const DELETE_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la supression du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const GETTING_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la récupération du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const MOVING_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors du déplacement du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const DUPLICATE_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la duplication du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
export const COPY_FOLDER_ERROR:AppErrorInfos = {
|
||||||
|
message: 'Une erreur s\'est produite lors de la copie du dossier.',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NOT_IMPLEMENTED:AppErrorInfos = {
|
||||||
|
message: 'Route not implemented yet!',
|
||||||
|
statusCode: HttpStatusCode.BAD_REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static ok(res, results) {200
|
||||||
|
// static badRequest(res, message) {HttpStatusCode.BAD_REQUEST
|
||||||
|
// static unauthorized(res, message) {HttpStatusCode.UNAUTHORIZED
|
||||||
|
// static notFound(res, message) {HttpStatusCode.NOT_FOUND
|
||||||
|
// static serverError(res, message) {505
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
//controller
|
//controller
|
||||||
const AppError = require('../middleware/AppError.js');
|
import AppError from '../middleware/AppError.js';
|
||||||
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
|
import { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } from '../constants/errorCodes';
|
||||||
|
import Folders from '../models/folders.js';
|
||||||
|
|
||||||
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||||
class FoldersController {
|
class FoldersController {
|
||||||
|
folders:Folders
|
||||||
|
|
||||||
constructor(foldersModel) {
|
constructor(foldersModel) {
|
||||||
this.folders = foldersModel;
|
this.folders = foldersModel;
|
||||||
|
|
@ -258,6 +260,4 @@ class FoldersController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default FoldersController
|
||||||
|
|
||||||
module.exports = FoldersController;
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
const AppError = require('../middleware/AppError.js');
|
import AppError from '../middleware/AppError.js';
|
||||||
const { MISSING_REQUIRED_PARAMETER, IMAGE_NOT_FOUND } = require('../constants/errorCodes');
|
import { MISSING_REQUIRED_PARAMETER, IMAGE_NOT_FOUND } from '../constants/errorCodes.js';
|
||||||
|
import Images from '../models/images.js';
|
||||||
|
|
||||||
class ImagesController {
|
class ImagesController {
|
||||||
|
images:Images
|
||||||
|
|
||||||
constructor(imagesModel) {
|
constructor(imagesModel) {
|
||||||
this.images = imagesModel;
|
this.images = imagesModel;
|
||||||
|
|
@ -52,4 +54,4 @@ class ImagesController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ImagesController;
|
export default ImagesController
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
const emailer = require('../config/email.js');
|
import emailer from '../config/email.js';
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
import AppError from'../middleware/AppError.js';
|
||||||
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR, DUPLICATE_QUIZ_ERROR, COPY_QUIZ_ERROR } = require('../constants/errorCodes');
|
import { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR, DUPLICATE_QUIZ_ERROR, COPY_QUIZ_ERROR } from'../constants/errorCodes.js';
|
||||||
|
import Folders from '../models/folders.js';
|
||||||
|
import Quiz from '../models/quiz.js';
|
||||||
|
|
||||||
class QuizController {
|
class QuizController {
|
||||||
|
folders:Folders
|
||||||
|
quizzes:Quiz
|
||||||
|
|
||||||
constructor(quizModel, foldersModel) {
|
constructor(quizModel, foldersModel) {
|
||||||
this.folders = foldersModel;
|
this.folders = foldersModel;
|
||||||
|
|
@ -207,7 +211,7 @@ class QuizController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method from the Quiz model to delete quizzes by folder ID
|
// Call the method from the Quiz model to delete quizzes by folder ID
|
||||||
await Quiz.deleteQuizzesByFolderId(folderId);
|
await this.quizzes.deleteQuizzesByFolderId(folderId);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Quizzes deleted successfully.'
|
message: 'Quizzes deleted successfully.'
|
||||||
|
|
@ -314,4 +318,4 @@ class QuizController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = QuizController;
|
export default QuizController
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import { GlideClient, GlideClientConfiguration } from '@valkey/valkey-glide';
|
import { GlideClient } from '@valkey/valkey-glide';
|
||||||
import {
|
import {
|
||||||
RoomInfo,
|
RoomInfo,
|
||||||
RoomOptions,
|
RoomOptions,
|
||||||
ProviderType,
|
ProviderType,
|
||||||
ProviderConfig
|
ProviderConfig
|
||||||
} from '../../types/room';
|
} from '../../types/room';
|
||||||
import { BaseRoomProvider } from './providers/base-provider';
|
import { BaseRoomProvider } from '../roomsProviders/base-provider';
|
||||||
import { ClusterRoomProvider } from './providers/cluster-provider';
|
import { ClusterRoomProvider } from '../roomsProviders/cluster-provider';
|
||||||
import { DockerRoomProvider } from './providers/docker-provider';
|
//import { DockerRoomProvider } from '../roomsProviders/docker-provider';
|
||||||
import { KubernetesRoomProvider } from './providers/kubernetes-provider';
|
//import { KubernetesRoomProvider } from '../roomsProviders/kubernetes-provider';
|
||||||
|
|
||||||
interface RoomManagerOptions {
|
interface RoomManagerOptions {
|
||||||
valkeyConfig?: GlideClientConfiguration;
|
|
||||||
provider?: ProviderType;
|
provider?: ProviderType;
|
||||||
providerOptions?: ProviderConfig;
|
providerOptions?: ProviderConfig;
|
||||||
}
|
}
|
||||||
|
|
@ -20,8 +19,8 @@ export class RoomManager {
|
||||||
private valkey: GlideClient;
|
private valkey: GlideClient;
|
||||||
private provider: BaseRoomProvider<RoomInfo>;
|
private provider: BaseRoomProvider<RoomInfo>;
|
||||||
|
|
||||||
constructor(options: RoomManagerOptions = {}) {
|
constructor(options: RoomManagerOptions = {}, valkeyClient:GlideClient) {
|
||||||
this.valkey = new GlideClient();
|
this.valkey = valkeyClient;
|
||||||
this.provider = this.createProvider(
|
this.provider = this.createProvider(
|
||||||
options.provider || process.env.ROOM_PROVIDER as ProviderType || 'cluster',
|
options.provider || process.env.ROOM_PROVIDER as ProviderType || 'cluster',
|
||||||
options.providerOptions
|
options.providerOptions
|
||||||
|
|
@ -36,11 +35,13 @@ export class RoomManager {
|
||||||
): BaseRoomProvider<RoomInfo> {
|
): BaseRoomProvider<RoomInfo> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'cluster':
|
case 'cluster':
|
||||||
return new ClusterRoomProvider(this.redis, options);
|
return new ClusterRoomProvider(this.valkey, options);
|
||||||
|
/*
|
||||||
case 'docker':
|
case 'docker':
|
||||||
return new DockerRoomProvider(this.redis, options);
|
return new DockerRoomProvider(this.redis, options);
|
||||||
case 'kubernetes':
|
case 'kubernetes':
|
||||||
return new KubernetesRoomProvider(this.redis, options);
|
return new KubernetesRoomProvider(this.redis, options);
|
||||||
|
*/
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown provider type: ${type}`);
|
throw new Error(`Unknown provider type: ${type}`);
|
||||||
}
|
}
|
||||||
|
|
@ -73,3 +74,5 @@ export class RoomManager {
|
||||||
return `room-${Math.random().toString(36).substr(2, 9)}`;
|
return `room-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = RoomManager;
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
const emailer = require('../config/email.js');
|
import emailer from'../config/email.js';
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
import jwt from'../middleware/jwtToken.js';
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
import AppError from'../middleware/AppError.js';
|
||||||
const { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR, DELETE_USER_ERROR } = require('../constants/errorCodes');
|
import { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR, DELETE_USER_ERROR, USER_NOT_FOUND,USER_CONTROLLER_NOT_INITIALIZED } from'../constants/errorCodes';
|
||||||
|
import Users from '../models/users.js'
|
||||||
|
|
||||||
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||||
class UsersController {
|
class UsersController {
|
||||||
|
users:Users
|
||||||
|
|
||||||
constructor(userModel) {
|
constructor(userModel) {
|
||||||
this.users = userModel;
|
this.users = userModel;
|
||||||
|
|
@ -20,7 +22,7 @@ class UsersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.users) {
|
if (!this.users) {
|
||||||
throw new AppError('Users model not found');
|
throw new AppError(USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
await this.users.register(email, password);
|
await this.users.register(email, password);
|
||||||
|
|
||||||
|
|
@ -43,7 +45,7 @@ class UsersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this) {
|
if (!this) {
|
||||||
throw new AppError('UsersController not initialized');
|
throw new AppError(USER_CONTROLLER_NOT_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.users.login(email, password);
|
const user = await this.users.login(email, password);
|
||||||
|
|
@ -143,4 +145,4 @@ class UsersController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UsersController;
|
export default UsersController;
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
class AppError extends Error {
|
|
||||||
constructor (errorCode) {
|
|
||||||
super(errorCode.message)
|
|
||||||
this.statusCode = errorCode.code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = AppError;
|
|
||||||
14
server/middleware/AppError.ts
Normal file
14
server/middleware/AppError.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { HttpStatusCode } from "../utils/http-status-codes"
|
||||||
|
|
||||||
|
export type AppErrorInfos = {
|
||||||
|
message:string
|
||||||
|
statusCode:number|HttpStatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AppError extends Error {
|
||||||
|
statusCode:number
|
||||||
|
constructor (infos:AppErrorInfos) {
|
||||||
|
super(infos.message)
|
||||||
|
this.statusCode = infos.statusCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
const AppError = require("./AppError");
|
import AppError from './AppError'
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
|
import { HttpStatusCode } from '../utils/http-status-codes';
|
||||||
|
|
||||||
const errorHandler = (error, req, res, next) => {
|
const errorHandler = (error, req, res, next) => {
|
||||||
console.log("ERROR", error);
|
console.log("ERROR", error);
|
||||||
|
|
@ -12,7 +13,7 @@ const errorHandler = (error, req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
logError(error.stack);
|
logError(error.stack);
|
||||||
return res.status(505).send("Oups! We screwed up big time. ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻");
|
return res.status(HttpStatusCode.INTERNAL_SERVER_ERROR).send("Oups! We screwed up big time. ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻");
|
||||||
}
|
}
|
||||||
|
|
||||||
const logError = (error) => {
|
const logError = (error) => {
|
||||||
|
|
@ -21,4 +22,4 @@ const logError = (error) => {
|
||||||
log_file.write(time + '\n' + error + '\n\n');
|
log_file.write(time + '\n' + error + '\n\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = errorHandler;
|
export default errorHandler
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const jwt = require('jsonwebtoken')
|
import jwt from 'jsonwebtoken'
|
||||||
const dotenv = require('dotenv')
|
import dotenv from 'dotenv'
|
||||||
const AppError = require('./AppError.js');
|
import AppError from './AppError.ts';
|
||||||
const { UNAUTHORIZED_NO_TOKEN_GIVEN, UNAUTHORIZED_INVALID_TOKEN } = require('../constants/errorCodes');
|
import { UNAUTHORIZED_NO_TOKEN_GIVEN, UNAUTHORIZED_INVALID_TOKEN } from '../constants/errorCodes';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
|
@ -34,4 +34,4 @@ class Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Token();
|
export default new Token()
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
//model
|
//model
|
||||||
const ObjectId = require('mongodb').ObjectId;
|
import type { DBConnection } from '../config/db';
|
||||||
const { generateUniqueTitle } = require('./utils');
|
import type {Quiz} from './quiz'
|
||||||
|
import {ObjectId} from 'mongodb'
|
||||||
|
import { generateUniqueTitle } from '../utils/models-utils';
|
||||||
|
|
||||||
class Folders {
|
class Folders {
|
||||||
|
|
||||||
|
db: DBConnection
|
||||||
|
quizModel:Quiz
|
||||||
|
|
||||||
constructor(db, quizModel) {
|
constructor(db, quizModel) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.quizModel = quizModel;
|
this.quizModel = quizModel;
|
||||||
|
|
@ -57,7 +63,7 @@ class Folders {
|
||||||
|
|
||||||
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||||
|
|
||||||
return folder.userId;
|
return folder?.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// finds all quizzes in a folder
|
// finds all quizzes in a folder
|
||||||
|
|
@ -151,7 +157,8 @@ class Folders {
|
||||||
async copy(folderId, userId) {
|
async copy(folderId, userId) {
|
||||||
|
|
||||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
const sourceFolder = await this.getFolderWithContent(folderId);
|
||||||
const newFolderId = await this.create(sourceFolder.title, userId);
|
|
||||||
|
const newFolderId = await this.create(sourceFolder[0].title, userId);
|
||||||
if (!newFolderId) {
|
if (!newFolderId) {
|
||||||
throw new Error('Failed to create a new folder.');
|
throw new Error('Failed to create a new folder.');
|
||||||
}
|
}
|
||||||
|
|
@ -192,4 +199,4 @@ class Folders {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Folders;
|
export default Folders
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
//const db = require('../config/db.js')
|
import type {DBConnection} from '../config/db.js'
|
||||||
const { ObjectId } = require('mongodb');
|
import { ObjectId } from 'mongodb';
|
||||||
|
|
||||||
class Images {
|
class Images {
|
||||||
|
db:DBConnection
|
||||||
|
|
||||||
constructor(db) {
|
constructor(db) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(file, userId) {
|
async upload(file, userId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const imagesCollection = conn.collection('images');
|
const imagesCollection = conn.collection('images');
|
||||||
|
|
||||||
|
|
@ -27,8 +28,8 @@ class Images {
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id) {
|
async get(id) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const imagesCollection = conn.collection('images');
|
const imagesCollection = conn.collection('images');
|
||||||
|
|
||||||
|
|
@ -45,4 +46,4 @@ class Images {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Images;
|
export default Images
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
const { ObjectId } = require('mongodb');
|
import type { DBConnection } from '../config/db';
|
||||||
const { generateUniqueTitle } = require('./utils');
|
import { ObjectId } from 'mongodb';
|
||||||
|
import { generateUniqueTitle } from '../utils/models-utils'
|
||||||
|
|
||||||
class Quiz {
|
export class Quiz {
|
||||||
|
|
||||||
|
db: DBConnection
|
||||||
|
|
||||||
constructor(db) {
|
constructor(db) {
|
||||||
// console.log("Quiz constructor: db", db)
|
// console.log("Quiz constructor: db", db)
|
||||||
|
|
@ -44,7 +47,7 @@ class Quiz {
|
||||||
|
|
||||||
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
|
||||||
return quiz.userId;
|
return quiz?.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContent(quizId) {
|
async getContent(quizId) {
|
||||||
|
|
@ -151,5 +154,4 @@ class Quiz {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
export default Quiz;
|
||||||
module.exports = Quiz;
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
//user
|
//user
|
||||||
const bcrypt = require('bcrypt');
|
import bcrypt from 'bcrypt';
|
||||||
const AppError = require('../middleware/AppError.js');
|
import AppError from '../middleware/AppError.js'
|
||||||
const { USER_ALREADY_EXISTS } = require('../constants/errorCodes');
|
import { USER_ALREADY_EXISTS } from '../constants/errorCodes.js';
|
||||||
|
import type { DBConnection } from '../config/db.js';
|
||||||
|
import Folders from './folders.js';
|
||||||
|
|
||||||
class Users {
|
class Users {
|
||||||
|
db:DBConnection
|
||||||
|
folders:Folders
|
||||||
|
|
||||||
constructor(db, foldersModel) {
|
constructor(db, foldersModel) {
|
||||||
// console.log("Users constructor: db", db)
|
// console.log("Users constructor: db", db)
|
||||||
this.db = db;
|
this.db = db;
|
||||||
|
|
@ -122,4 +127,4 @@ class Users {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Users;
|
export default Users
|
||||||
2404
server/package-lock.json
generated
2404
server/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,17 +3,18 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "tsc",
|
||||||
"start": "node app.js",
|
"start": "node dist/app.js",
|
||||||
"dev": "cross-env NODE_ENV=development nodemon app.js",
|
"dev": "ts-node app.ts",
|
||||||
"test": "jest --colors"
|
"watch": "tsc -w"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^5.0.0",
|
|
||||||
"@valkey/valkey-glide": "^1.1.0",
|
"@valkey/valkey-glide": "^1.1.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
|
@ -27,12 +28,19 @@
|
||||||
"socket.io-client": "^4.7.2"
|
"socket.io-client": "^4.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.8.4",
|
"@types/body-parser": "^1.19.5",
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/multer": "^1.4.12",
|
||||||
|
"@types/node": "^22.9.0",
|
||||||
|
"@types/socket.io": "^3.0.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-mock": "^29.7.0",
|
"jest-mock": "^29.7.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^6.3.4",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.6.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18.x"
|
"node": "18.x"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
const express = require('express');
|
import express, { Response, Request } from "express";
|
||||||
|
import jwt from '../middleware/jwtToken.js';
|
||||||
|
import {controllers} from '../app.js'
|
||||||
|
|
||||||
|
const folders = controllers.folders
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
|
||||||
const folders = require('../app.js').folders;
|
|
||||||
|
|
||||||
router.post("/create", jwt.authenticate, folders.create);
|
router.post("/create", jwt.authenticate, folders.create);
|
||||||
router.get("/getUserFolders", jwt.authenticate, folders.getUserFolders);
|
router.get("/getUserFolders", jwt.authenticate, folders.getUserFolders);
|
||||||
|
|
@ -14,7 +16,7 @@ router.post("/duplicate", jwt.authenticate, folders.duplicate);
|
||||||
|
|
||||||
router.post("/copy/:folderId", jwt.authenticate, folders.copy);
|
router.post("/copy/:folderId", jwt.authenticate, folders.copy);
|
||||||
|
|
||||||
module.exports = router;
|
export default router
|
||||||
|
|
||||||
// export also folders (the controller)
|
// export also folders (the controller)
|
||||||
module.exports.folders = folders;
|
module.exports.folders = folders;
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
const express = require('express');
|
import express, { Response, Request } from "express";
|
||||||
const router = express.Router();
|
import jwt from '../middleware/jwtToken.js';
|
||||||
const images = require('../app.js').images;
|
import {controllers} from '../app.js'
|
||||||
|
import multer from 'multer';
|
||||||
|
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const images = controllers.images
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
// For getting the image out of the form data
|
// For getting the image out of the form data
|
||||||
const multer = require('multer');
|
|
||||||
const storage = multer.memoryStorage();
|
const storage = multer.memoryStorage();
|
||||||
const upload = multer({ storage: storage });
|
const upload = multer({ storage: storage });
|
||||||
|
|
||||||
router.post("/upload", jwt.authenticate, upload.single('image'), images.upload);
|
router.post("/upload", jwt.authenticate, upload.single('image'), images.upload);
|
||||||
router.get("/get/:id", images.get);
|
router.get("/get/:id", images.get);
|
||||||
|
|
||||||
module.exports = router;
|
export default router
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
const express = require('express');
|
import express, { Response, Request } from "express";
|
||||||
|
import jwt from '../middleware/jwtToken.js';
|
||||||
|
import {controllers} from '../app.js'
|
||||||
|
|
||||||
|
const quizzes = controllers.quizzes
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const quizzes = require('../app.js').quizzes;
|
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
|
||||||
|
|
||||||
if (!quizzes) {
|
if (!quizzes) {
|
||||||
console.error("quizzes is not defined");
|
console.error("quizzes is not defined");
|
||||||
|
|
@ -19,4 +21,4 @@ router.put("/Share", jwt.authenticate, quizzes.share);
|
||||||
router.get("/getShare/:quizId", jwt.authenticate, quizzes.getShare);
|
router.get("/getShare/:quizId", jwt.authenticate, quizzes.getShare);
|
||||||
router.post("/receiveShare", jwt.authenticate, quizzes.receiveShare);
|
router.post("/receiveShare", jwt.authenticate, quizzes.receiveShare);
|
||||||
|
|
||||||
module.exports = router;
|
export default router
|
||||||
|
|
@ -1 +1,13 @@
|
||||||
import {express} from 'express'
|
import express, { Response, Request } from "express";
|
||||||
|
import jwt from '../middleware/jwtToken.js';
|
||||||
|
import {controllers} from '../app.js'
|
||||||
|
|
||||||
|
const roomsController = controllers.rooms
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/", jwt.authenticate, async(req:Request,res:Response)=>{
|
||||||
|
const data = await roomsController.listRooms();
|
||||||
|
res.json(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
const express = require('express');
|
import express, { Response, Request } from "express";
|
||||||
|
import jwt from '../middleware/jwtToken.js';
|
||||||
|
import {controllers} from '../app.js'
|
||||||
|
|
||||||
|
const users = controllers.users
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const users = require('../app.js').users;
|
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
|
||||||
|
|
||||||
router.post("/register", users.register);
|
router.post("/register", users.register);
|
||||||
router.post("/login", users.login);
|
router.post("/login", users.login);
|
||||||
|
|
@ -9,4 +11,4 @@ router.post("/reset-password", users.resetPassword);
|
||||||
router.post("/change-password", jwt.authenticate, users.changePassword);
|
router.post("/change-password", jwt.authenticate, users.changePassword);
|
||||||
router.post("/delete-user", jwt.authenticate, users.delete);
|
router.post("/delete-user", jwt.authenticate, users.delete);
|
||||||
|
|
||||||
module.exports = router;
|
export default router
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { Server as SocketIOServer } from 'socket.io';
|
||||||
|
|
||||||
const MAX_USERS_PER_ROOM = 60;
|
const MAX_USERS_PER_ROOM = 60;
|
||||||
const MAX_TOTAL_CONNECTIONS = 2000;
|
const MAX_TOTAL_CONNECTIONS = 2000;
|
||||||
|
|
||||||
const setupWebsocket = (io) => {
|
const setupWebsocket = (io: SocketIOServer) => {
|
||||||
let totalConnections = 0;
|
let totalConnections = 0;
|
||||||
|
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
|
|
@ -46,7 +48,7 @@ const setupWebsocket = (io) => {
|
||||||
socket.on("join-room", ({ enteredRoomName, username }) => {
|
socket.on("join-room", ({ enteredRoomName, username }) => {
|
||||||
if (io.sockets.adapter.rooms.has(enteredRoomName)) {
|
if (io.sockets.adapter.rooms.has(enteredRoomName)) {
|
||||||
const clientsInRoom =
|
const clientsInRoom =
|
||||||
io.sockets.adapter.rooms.get(enteredRoomName).size;
|
io.sockets.adapter.rooms.get(enteredRoomName)?.size ?? 0;
|
||||||
|
|
||||||
if (clientsInRoom <= MAX_USERS_PER_ROOM) {
|
if (clientsInRoom <= MAX_USERS_PER_ROOM) {
|
||||||
const newStudent = {
|
const newStudent = {
|
||||||
|
|
@ -122,4 +124,4 @@ const setupWebsocket = (io) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { setupWebsocket };
|
export default setupWebsocket
|
||||||
29
server/tsconfig.json
Normal file
29
server/tsconfig.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"module": "es2022",
|
||||||
|
"lib": ["es2017", "esnext.asynciterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"removeComments": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"rootDir": "."
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules"],
|
||||||
|
"include": ["./**/*.ts"]
|
||||||
|
}
|
||||||
41
server/utils/http-status-codes.ts
Normal file
41
server/utils/http-status-codes.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
export enum HttpStatusCode {
|
||||||
|
// 1xx Informational
|
||||||
|
CONTINUE = 100,
|
||||||
|
SWITCHING_PROTOCOLS = 101,
|
||||||
|
PROCESSING = 102,
|
||||||
|
|
||||||
|
// 2xx Success
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
RESET_CONTENT = 205,
|
||||||
|
PARTIAL_CONTENT = 206,
|
||||||
|
|
||||||
|
// 3xx Redirection
|
||||||
|
MULTIPLE_CHOICES = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
FOUND = 302,
|
||||||
|
SEE_OTHER = 303,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
USE_PROXY = 305,
|
||||||
|
TEMPORARY_REDIRECT = 307,
|
||||||
|
PERMANENT_REDIRECT = 308,
|
||||||
|
|
||||||
|
// 4xx Client errors
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
PAYMENT_REQUIRED = 402,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
METHOD_NOT_ALLOWED = 405,
|
||||||
|
NOT_ACCEPTABLE = 406,
|
||||||
|
|
||||||
|
// 5xx Client errors
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503,
|
||||||
|
GATEWAY_TIMEOUT = 504,
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
// utils.js
|
// utils.js
|
||||||
async function generateUniqueTitle(baseTitle, existsCallback) {
|
export async function generateUniqueTitle(baseTitle:string, existsCallback:CallableFunction) {
|
||||||
console.log(`generateUniqueTitle(${baseTitle})`);
|
console.log(`generateUniqueTitle(${baseTitle})`);
|
||||||
let newTitle = baseTitle;
|
let newTitle = baseTitle;
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
|
|
@ -12,7 +14,7 @@ async function generateUniqueTitle(baseTitle, existsCallback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the base title does not end with a parentheses expression, start with "(1)"
|
// If the base title does not end with a parentheses expression, start with "(1)"
|
||||||
if (!match[2]) {
|
if (match != null && match[2] != null) {
|
||||||
newTitle = `${baseTitle} (${counter})`;
|
newTitle = `${baseTitle} (${counter})`;
|
||||||
} else {
|
} else {
|
||||||
// else increment the counter in the parentheses expression as a first try
|
// else increment the counter in the parentheses expression as a first try
|
||||||
|
|
@ -29,7 +31,3 @@ async function generateUniqueTitle(baseTitle, existsCallback) {
|
||||||
|
|
||||||
return newTitle;
|
return newTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
generateUniqueTitle
|
|
||||||
};
|
|
||||||
0
types/room.ts → types/room.d.ts
vendored
0
types/room.ts → types/room.d.ts
vendored
|
|
@ -2,8 +2,3 @@ export interface ValkeyBasicConfig{
|
||||||
host:string,
|
host:string,
|
||||||
port:number
|
port:number
|
||||||
}
|
}
|
||||||
|
|
||||||
const valkeyDefaults : ValkeyBasicConfig = {
|
|
||||||
host:'localhost',
|
|
||||||
port:6379
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue