@programisto/edrm-storage 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +135 -0
  2. package/dist/bin/www.d.ts +2 -0
  3. package/dist/bin/www.js +13 -0
  4. package/dist/modules/edrm-exams/lib/openai/correctQuestion.txt +9 -0
  5. package/dist/modules/edrm-exams/lib/openai/createQuestion.txt +6 -0
  6. package/dist/modules/edrm-exams/lib/openai.d.ts +37 -0
  7. package/dist/modules/edrm-exams/lib/openai.js +135 -0
  8. package/dist/modules/edrm-exams/listeners/correct.listener.d.ts +2 -0
  9. package/dist/modules/edrm-exams/listeners/correct.listener.js +167 -0
  10. package/dist/modules/edrm-exams/models/candidate.model.d.ts +21 -0
  11. package/dist/modules/edrm-exams/models/candidate.model.js +75 -0
  12. package/dist/modules/edrm-exams/models/candidate.models.d.ts +21 -0
  13. package/dist/modules/edrm-exams/models/candidate.models.js +75 -0
  14. package/dist/modules/edrm-exams/models/company.model.d.ts +8 -0
  15. package/dist/modules/edrm-exams/models/company.model.js +34 -0
  16. package/dist/modules/edrm-exams/models/contact.model.d.ts +14 -0
  17. package/dist/modules/edrm-exams/models/contact.model.js +60 -0
  18. package/dist/modules/edrm-exams/models/test-category.models.d.ts +7 -0
  19. package/dist/modules/edrm-exams/models/test-category.models.js +29 -0
  20. package/dist/modules/edrm-exams/models/test-job.model.d.ts +7 -0
  21. package/dist/modules/edrm-exams/models/test-job.model.js +29 -0
  22. package/dist/modules/edrm-exams/models/test-question.model.d.ts +25 -0
  23. package/dist/modules/edrm-exams/models/test-question.model.js +70 -0
  24. package/dist/modules/edrm-exams/models/test-result.model.d.ts +26 -0
  25. package/dist/modules/edrm-exams/models/test-result.model.js +70 -0
  26. package/dist/modules/edrm-exams/models/test.model.d.ts +47 -0
  27. package/dist/modules/edrm-exams/models/test.model.js +133 -0
  28. package/dist/modules/edrm-exams/models/user.model.d.ts +18 -0
  29. package/dist/modules/edrm-exams/models/user.model.js +73 -0
  30. package/dist/modules/edrm-exams/routes/company.router.d.ts +7 -0
  31. package/dist/modules/edrm-exams/routes/company.router.js +108 -0
  32. package/dist/modules/edrm-exams/routes/exams-candidate.router.d.ts +7 -0
  33. package/dist/modules/edrm-exams/routes/exams-candidate.router.js +448 -0
  34. package/dist/modules/edrm-exams/routes/exams.router.d.ts +8 -0
  35. package/dist/modules/edrm-exams/routes/exams.router.js +1343 -0
  36. package/dist/modules/edrm-exams/routes/result.router.d.ts +7 -0
  37. package/dist/modules/edrm-exams/routes/result.router.js +370 -0
  38. package/dist/modules/edrm-exams/routes/user.router.d.ts +7 -0
  39. package/dist/modules/edrm-exams/routes/user.router.js +96 -0
  40. package/dist/modules/edrm-storage/config/edrm-storage.config.d.ts +29 -0
  41. package/dist/modules/edrm-storage/config/edrm-storage.config.js +31 -0
  42. package/dist/modules/edrm-storage/config/environment.example.d.ts +54 -0
  43. package/dist/modules/edrm-storage/config/environment.example.js +130 -0
  44. package/dist/modules/edrm-storage/examples/usage.example.d.ts +52 -0
  45. package/dist/modules/edrm-storage/examples/usage.example.js +156 -0
  46. package/dist/modules/edrm-storage/index.d.ts +5 -0
  47. package/dist/modules/edrm-storage/index.js +8 -0
  48. package/dist/modules/edrm-storage/integration/edrm-storage-integration.d.ts +53 -0
  49. package/dist/modules/edrm-storage/integration/edrm-storage-integration.js +132 -0
  50. package/dist/modules/edrm-storage/interfaces/storage-provider.interface.d.ts +35 -0
  51. package/dist/modules/edrm-storage/interfaces/storage-provider.interface.js +1 -0
  52. package/dist/modules/edrm-storage/migrations/edrm-storage.migration.d.ts +6 -0
  53. package/dist/modules/edrm-storage/migrations/edrm-storage.migration.js +151 -0
  54. package/dist/modules/edrm-storage/models/file.model.d.ts +78 -0
  55. package/dist/modules/edrm-storage/models/file.model.js +190 -0
  56. package/dist/modules/edrm-storage/providers/s3-storage.provider.d.ts +18 -0
  57. package/dist/modules/edrm-storage/providers/s3-storage.provider.js +95 -0
  58. package/dist/modules/edrm-storage/routes/edrm-storage.router.d.ts +8 -0
  59. package/dist/modules/edrm-storage/routes/edrm-storage.router.js +155 -0
  60. package/dist/modules/edrm-storage/scripts/quick-start.d.ts +7 -0
  61. package/dist/modules/edrm-storage/scripts/quick-start.js +114 -0
  62. package/dist/modules/edrm-storage/services/edrm-storage.service.d.ts +29 -0
  63. package/dist/modules/edrm-storage/services/edrm-storage.service.js +188 -0
  64. package/dist/modules/edrm-storage/tests/edrm-storage.service.test.d.ts +1 -0
  65. package/dist/modules/edrm-storage/tests/edrm-storage.service.test.js +143 -0
  66. package/dist/modules/edrm-storage/tests/integration.test.d.ts +1 -0
  67. package/dist/modules/edrm-storage/tests/integration.test.js +141 -0
  68. package/package.json +81 -0
@@ -0,0 +1,7 @@
1
+ import { EnduranceRouter } from '@programisto/endurance-core';
2
+ declare class ResultRouter extends EnduranceRouter {
3
+ constructor();
4
+ setupRoutes(): void;
5
+ }
6
+ declare const router: ResultRouter;
7
+ export default router;
@@ -0,0 +1,370 @@
1
+ import { EnduranceRouter, EnduranceAuthMiddleware, enduranceEmitter, enduranceEventTypes } from '@programisto/endurance-core';
2
+ import CandidateModel from '../models/candidate.model.js';
3
+ import TestResult, { TestState } from '../models/test-result.model.js';
4
+ import Test from '../models/test.model.js';
5
+ import TestJob from '../models/test-job.model.js';
6
+ // Fonction utilitaire pour récupérer le nom du job
7
+ async function getJobName(targetJob) {
8
+ // Si c'est déjà une string (ancien format), on la retourne directement
9
+ if (typeof targetJob === 'string') {
10
+ return targetJob;
11
+ }
12
+ // Si c'est un ObjectId, on récupère le job
13
+ if (targetJob && typeof targetJob === 'object' && targetJob._id) {
14
+ const job = await TestJob.findById(targetJob._id);
15
+ return job ? job.name : 'Job inconnu';
16
+ }
17
+ // Si c'est juste un ObjectId
18
+ if (targetJob && typeof targetJob === 'object' && targetJob.toString) {
19
+ const job = await TestJob.findById(targetJob);
20
+ return job ? job.name : 'Job inconnu';
21
+ }
22
+ return 'Job inconnu';
23
+ }
24
+ class ResultRouter extends EnduranceRouter {
25
+ constructor() {
26
+ super(EnduranceAuthMiddleware.getInstance());
27
+ }
28
+ setupRoutes() {
29
+ const authenticatedOptions = {
30
+ requireAuth: false,
31
+ permissions: []
32
+ };
33
+ // Lister tous les résultats de tests d'un candidat
34
+ this.get('/results/:candidateId', authenticatedOptions, async (req, res) => {
35
+ try {
36
+ const { candidateId } = req.params;
37
+ const page = parseInt(req.query.page) || 1;
38
+ const limit = parseInt(req.query.limit) || 10;
39
+ const skip = (page - 1) * limit;
40
+ const state = req.query.state || 'all';
41
+ const sortBy = req.query.sortBy || 'invitationDate';
42
+ const sortOrder = req.query.sortOrder || 'desc';
43
+ // Vérifier si le candidat existe
44
+ const candidate = await CandidateModel.findById(candidateId);
45
+ if (!candidate) {
46
+ return res.status(404).json({ message: 'Candidat non trouvé' });
47
+ }
48
+ // Construction de la requête
49
+ const query = { candidateId };
50
+ if (state !== 'all') {
51
+ query.state = state;
52
+ }
53
+ // Construction du tri
54
+ const allowedSortFields = ['invitationDate', 'state', 'score'];
55
+ const sortField = allowedSortFields.includes(sortBy) ? sortBy : 'invitationDate';
56
+ const sortOptions = {
57
+ [sortField]: sortOrder === 'asc' ? 1 : -1
58
+ };
59
+ const [results, total] = await Promise.all([
60
+ TestResult.find(query)
61
+ .sort(sortOptions)
62
+ .skip(skip)
63
+ .limit(limit)
64
+ .lean()
65
+ .exec(),
66
+ TestResult.countDocuments(query)
67
+ ]);
68
+ // Récupérer les informations des tests associés
69
+ const testIds = results.map(result => result.testId);
70
+ const tests = await Test.find({ _id: { $in: testIds } }).lean();
71
+ const testsMap = new Map(tests.map(test => [test._id.toString(), test]));
72
+ // Récupérer tous les IDs de catégories utilisés dans les tests
73
+ const allCategoryIds = Array.from(new Set(tests.flatMap(test => (test.categories || []).map((cat) => cat.categoryId?.toString()))));
74
+ const TestCategory = (await import('../models/test-category.models.js')).default;
75
+ const categoriesDocs = await TestCategory.find({ _id: { $in: allCategoryIds } }).lean();
76
+ const categoriesMap = new Map(categoriesDocs.map(cat => [cat._id.toString(), cat.name]));
77
+ // Combiner les résultats avec les informations des tests et des catégories
78
+ const TestQuestion = (await import('../models/test-question.model.js')).default;
79
+ const resultsWithTests = await Promise.all(results.map(async (result) => {
80
+ const test = testsMap.get(result.testId.toString());
81
+ let categoriesWithNames = [];
82
+ let maxScore = 0;
83
+ if (test && test.categories) {
84
+ categoriesWithNames = test.categories.map((cat) => ({
85
+ ...cat,
86
+ categoryName: categoriesMap.get(cat.categoryId?.toString()) || 'Catégorie inconnue'
87
+ }));
88
+ }
89
+ if (test && test.questions && test.questions.length > 0) {
90
+ const questionIds = test.questions.map((q) => q.questionId || q);
91
+ const questions = await TestQuestion.find({ _id: { $in: questionIds } }).lean();
92
+ maxScore = questions.reduce((sum, q) => sum + (q.maxScore || 0), 0);
93
+ }
94
+ const { responses, ...resultWithoutResponses } = result;
95
+ return {
96
+ ...resultWithoutResponses,
97
+ testResultId: result._id,
98
+ maxScore,
99
+ test: test
100
+ ? {
101
+ title: test.title,
102
+ description: test.description,
103
+ targetJob: await getJobName(test.targetJob),
104
+ seniorityLevel: test.seniorityLevel,
105
+ categories: categoriesWithNames
106
+ }
107
+ : null
108
+ };
109
+ }));
110
+ const totalPages = Math.ceil(total / limit);
111
+ return res.json({
112
+ data: resultsWithTests,
113
+ pagination: {
114
+ currentPage: page,
115
+ totalPages,
116
+ totalItems: total,
117
+ itemsPerPage: limit,
118
+ hasNextPage: page < totalPages,
119
+ hasPreviousPage: page > 1
120
+ }
121
+ });
122
+ }
123
+ catch (err) {
124
+ console.error('Erreur lors de la récupération des résultats :', err);
125
+ res.status(500).json({ message: 'Erreur interne du serveur' });
126
+ }
127
+ });
128
+ // Obtenir les infos de base d'un test (sans les questions), avec categoryName et maxTime
129
+ this.get('/test/:id', authenticatedOptions, async (req, res) => {
130
+ try {
131
+ const { id } = req.params;
132
+ const TestCategory = (await import('../models/test-category.models.js')).default;
133
+ const TestQuestion = (await import('../models/test-question.model.js')).default;
134
+ // Récupérer le test sans les questions
135
+ const test = await Test.findById(id).lean();
136
+ if (!test) {
137
+ return res.status(404).json({ message: 'Test non trouvé' });
138
+ }
139
+ // Récupérer les noms des catégories
140
+ const categoryIds = (test.categories || []).map((cat) => cat.categoryId?.toString());
141
+ const categoriesDocs = await TestCategory.find({ _id: { $in: categoryIds } }).lean();
142
+ const categoriesMap = new Map(categoriesDocs.map(cat => [cat._id.toString(), cat.name]));
143
+ const categoriesWithNames = (test.categories || []).map((cat) => ({
144
+ ...cat,
145
+ categoryName: categoriesMap.get(cat.categoryId?.toString()) || 'Catégorie inconnue'
146
+ }));
147
+ // Calculer la somme du temps de toutes les questions
148
+ const questions = await TestQuestion.find({ _id: { $in: (test.questions || []).map((q) => q.questionId) } }).lean();
149
+ const maxTime = questions.reduce((sum, q) => sum + (q.time || 0), 0);
150
+ const numberOfQuestions = questions.length;
151
+ // Récupérer le nom du job
152
+ const targetJobName = await getJobName(test.targetJob);
153
+ // Construire la réponse sans les questions
154
+ const { questions: _questions, // on retire les questions
155
+ ...testWithoutQuestions } = test;
156
+ return res.json({
157
+ ...testWithoutQuestions,
158
+ targetJobName,
159
+ categories: categoriesWithNames,
160
+ maxTime,
161
+ numberOfQuestions
162
+ });
163
+ }
164
+ catch (err) {
165
+ console.error('Erreur lors de la récupération du test :', err);
166
+ res.status(500).json({ message: 'Erreur interne du serveur' });
167
+ }
168
+ });
169
+ // Obtenir l'ID de la prochaine question non répondue pour un résultat de test
170
+ this.get('/:id/nextQuestion', authenticatedOptions, async (req, res) => {
171
+ try {
172
+ const { id } = req.params;
173
+ const { currentQuestionId } = req.query;
174
+ // Récupérer le résultat de test
175
+ const result = await TestResult.findById(id);
176
+ if (!result) {
177
+ return res.status(404).json({ message: 'Résultat non trouvé' });
178
+ }
179
+ // Récupérer le test associé
180
+ const test = await Test.findById(result.testId).lean();
181
+ if (!test) {
182
+ return res.status(404).json({ message: 'Test non trouvé' });
183
+ }
184
+ // Liste des questions du test dans l'ordre
185
+ const questions = test.questions || [];
186
+ if (currentQuestionId) {
187
+ // Si on a un currentQuestionId, on cherche la question suivante dans l'ordre
188
+ const currentIndex = questions.findIndex(q => (q.questionId ? q.questionId.toString() : q.toString()) === currentQuestionId);
189
+ if (currentIndex === -1) {
190
+ return res.status(404).json({ message: 'Question courante non trouvée' });
191
+ }
192
+ // Si c'est la dernière question
193
+ if (currentIndex === questions.length - 1) {
194
+ // On est sur la dernière réponse, on met à jour la date de fin
195
+ result.endTime = new Date();
196
+ await result.save();
197
+ return res.json({ nextQuestionId: 'result' });
198
+ }
199
+ // Retourner la question suivante
200
+ const nextQuestion = questions[currentIndex + 1];
201
+ // Si c'est la première question (currentIndex === -1 avant), on met à jour la date de début
202
+ if (currentIndex === 0 && !result.startTime) {
203
+ result.startTime = new Date();
204
+ await result.save();
205
+ }
206
+ return res.json({
207
+ nextQuestionId: nextQuestion.questionId
208
+ ? nextQuestion.questionId.toString()
209
+ : nextQuestion.toString()
210
+ });
211
+ }
212
+ else {
213
+ // Comportement original : chercher la première question non répondue
214
+ const answeredIds = (result.responses || []).map((r) => r.questionId.toString());
215
+ let nextQuestionId = null;
216
+ for (const q of questions) {
217
+ const qid = (q.questionId ? q.questionId.toString() : q.toString());
218
+ if (!answeredIds.includes(qid)) {
219
+ nextQuestionId = qid;
220
+ break;
221
+ }
222
+ }
223
+ if (!nextQuestionId) {
224
+ // Plus de question à répondre, on met à jour la date de fin
225
+ result.endTime = new Date();
226
+ await result.save();
227
+ nextQuestionId = 'result';
228
+ }
229
+ else if (questions.length > 0 && nextQuestionId === (questions[0].questionId ? questions[0].questionId.toString() : questions[0].toString()) && !result.startTime) {
230
+ // Si c'est la première question, on met à jour la date de début
231
+ result.startTime = new Date();
232
+ await result.save();
233
+ }
234
+ return res.json({ nextQuestionId });
235
+ }
236
+ }
237
+ catch (err) {
238
+ console.error('Erreur lors de la récupération de la prochaine question :', err);
239
+ res.status(500).json({ message: 'Erreur interne du serveur' });
240
+ }
241
+ });
242
+ // Afficher une question par son ID (optionnellement vérifier la session)
243
+ this.get('/question/:idQuestion', authenticatedOptions, async (req, res) => {
244
+ try {
245
+ const { idQuestion } = req.params;
246
+ const { sessionId } = req.query;
247
+ const TestQuestion = (await import('../models/test-question.model.js')).default;
248
+ // Récupérer la question
249
+ const question = await TestQuestion.findById(idQuestion).lean();
250
+ if (!question) {
251
+ return res.status(404).json({ message: 'Question non trouvée' });
252
+ }
253
+ // Optionnel : vérifier que la question appartient bien au test de la session et n'a pas déjà été répondue
254
+ let test = null;
255
+ let questionPosition = -1;
256
+ let numberOfQuestions = 0;
257
+ if (sessionId) {
258
+ const result = await TestResult.findById(sessionId).lean();
259
+ if (!result) {
260
+ return res.status(404).json({ message: 'Session (résultat) non trouvée' });
261
+ }
262
+ test = await Test.findById(result.testId).lean();
263
+ if (!test) {
264
+ return res.status(404).json({ message: 'Test non trouvé' });
265
+ }
266
+ const questionIds = (test.questions || []).map((q) => q.questionId?.toString());
267
+ if (!questionIds.includes(idQuestion)) {
268
+ return res.status(403).json({ message: 'Question non autorisée pour cette session' });
269
+ }
270
+ // Vérifier que la question n'a pas déjà été répondue
271
+ const alreadyAnswered = (result.responses || []).some((r) => r.questionId?.toString() === idQuestion);
272
+ if (alreadyAnswered) {
273
+ return res.status(403).json({ message: 'Question déjà répondue pour cette session' });
274
+ }
275
+ }
276
+ else {
277
+ // Si pas de sessionId, on doit quand même récupérer le test pour avoir les infos
278
+ // Chercher dans tous les tests pour trouver celui qui contient cette question
279
+ const allTests = await Test.find({}).lean();
280
+ for (const t of allTests) {
281
+ const questionIds = (t.questions || []).map((q) => q.questionId?.toString());
282
+ if (questionIds.includes(idQuestion)) {
283
+ test = t;
284
+ break;
285
+ }
286
+ }
287
+ }
288
+ // Calculer la position de la question et le nombre total de questions
289
+ if (test) {
290
+ numberOfQuestions = test.questions?.length || 0;
291
+ const questionIndex = test.questions?.findIndex((q) => q.questionId?.toString() === idQuestion);
292
+ questionPosition = questionIndex !== -1 ? questionIndex + 1 : -1; // +1 car les positions commencent à 1
293
+ }
294
+ return res.json({
295
+ question,
296
+ numberOfQuestions,
297
+ questionPosition
298
+ });
299
+ }
300
+ catch (err) {
301
+ console.error('Erreur lors de la récupération de la question :', err);
302
+ res.status(500).json({ message: 'Erreur interne du serveur' });
303
+ }
304
+ });
305
+ // Enregistrer la réponse à une question pour un résultat de test
306
+ this.post('/response', authenticatedOptions, async (req, res) => {
307
+ try {
308
+ const { response, questionId, testResultId } = req.body;
309
+ // Récupérer le résultat de test
310
+ const result = await TestResult.findById(testResultId);
311
+ if (!result) {
312
+ return res.status(404).json({ message: 'Résultat non trouvé' });
313
+ }
314
+ // Récupérer le test associé
315
+ const test = await Test.findById(result.testId);
316
+ if (!test) {
317
+ return res.status(404).json({ message: 'Test non trouvé' });
318
+ }
319
+ // Vérifier que la question appartient bien au test
320
+ const questionIds = (test.questions || []).map((q) => q.questionId?.toString());
321
+ if (!questionIds.includes(questionId)) {
322
+ return res.status(403).json({ message: 'Question non autorisée pour ce test' });
323
+ }
324
+ // Vérifier que la question n'a pas déjà été répondue
325
+ const alreadyAnswered = (result.responses || []).some((r) => r.questionId?.toString() === questionId);
326
+ if (alreadyAnswered) {
327
+ return res.status(403).json({ message: 'Question déjà répondue pour cette session' });
328
+ }
329
+ // Enregistrer la réponse
330
+ result.responses = result.responses || [];
331
+ result.responses.push({
332
+ questionId,
333
+ response,
334
+ score: 0,
335
+ comment: ''
336
+ });
337
+ // Marquer explicitement le champ responses comme modifié
338
+ result.markModified('responses');
339
+ console.log('Avant sauvegarde - Responses:', result.responses);
340
+ // Vérifier si c'était la dernière question
341
+ const totalQuestions = test.questions.length;
342
+ const answeredQuestions = result.responses.length;
343
+ if (answeredQuestions === totalQuestions) {
344
+ result.state = TestState.Finish;
345
+ }
346
+ else {
347
+ result.state = TestState.InProgress;
348
+ }
349
+ // Sauvegarder d'abord la réponse
350
+ const savedResult = await result.save();
351
+ console.log('Après sauvegarde - Responses:', savedResult.responses);
352
+ // Déclencher la correction automatique seulement après la sauvegarde
353
+ if (answeredQuestions === totalQuestions) {
354
+ await enduranceEmitter.emit(enduranceEventTypes.CORRECT_TEST, savedResult);
355
+ }
356
+ return res.status(200).json({
357
+ message: 'Réponse enregistrée',
358
+ response,
359
+ isLastQuestion: answeredQuestions === totalQuestions
360
+ });
361
+ }
362
+ catch (err) {
363
+ console.error('Erreur lors de l\'enregistrement de la réponse :', err);
364
+ res.status(500).json({ message: 'Erreur interne du serveur' });
365
+ }
366
+ });
367
+ }
368
+ }
369
+ const router = new ResultRouter();
370
+ export default router;
@@ -0,0 +1,7 @@
1
+ import { EnduranceRouter } from '@programisto/endurance-core';
2
+ declare class UserRouter extends EnduranceRouter {
3
+ constructor();
4
+ setupRoutes(): void;
5
+ }
6
+ declare const router: UserRouter;
7
+ export default router;
@@ -0,0 +1,96 @@
1
+ import { EnduranceRouter, EnduranceAuthMiddleware } from '@programisto/endurance-core';
2
+ import UserExam from '../models/user.model.js';
3
+ class UserRouter extends EnduranceRouter {
4
+ constructor() {
5
+ super(EnduranceAuthMiddleware.getInstance());
6
+ }
7
+ setupRoutes() {
8
+ const authenticatedOptions = {
9
+ requireAuth: false,
10
+ permissions: []
11
+ };
12
+ // Lister tous les utilisateurs
13
+ this.get('/', authenticatedOptions, async (req, res) => {
14
+ try {
15
+ const users = await UserExam.find();
16
+ res.status(200).json({ array: users });
17
+ }
18
+ catch (err) {
19
+ console.error('Error when retrieving users: ', err);
20
+ res.status(500).json({ message: 'Internal server error' });
21
+ }
22
+ });
23
+ // Créer un utilisateur
24
+ this.post('/create', authenticatedOptions, async (req, res) => {
25
+ const { firstName, lastName, email, companyId } = req.body;
26
+ if (!firstName || !lastName || !email || !companyId) {
27
+ return res.status(400).json({ message: 'Error, firstName, lastName, email and companyId are required' });
28
+ }
29
+ try {
30
+ const newUser = new UserExam({ firstName, lastName, email, companyId });
31
+ await newUser.save();
32
+ res.status(201).json({ message: 'user created with sucess', user: newUser });
33
+ }
34
+ catch (err) {
35
+ console.error('error when creating user : ', err);
36
+ res.status(500).json({ message: 'Internal server error' });
37
+ }
38
+ });
39
+ // Obtenir un utilisateur par son ID
40
+ this.get('/:id', authenticatedOptions, async (req, res) => {
41
+ const { id } = req.params;
42
+ try {
43
+ const user = await UserExam.findById(id);
44
+ if (!user) {
45
+ return res.status(404).json({ message: 'no user founded with this id' });
46
+ }
47
+ res.status(200).json({ data: user });
48
+ }
49
+ catch (err) {
50
+ console.error('error when geting user : ', err);
51
+ res.status(500).json({ message: 'Internal server error' });
52
+ }
53
+ });
54
+ // Mettre à jour un utilisateur
55
+ this.put('/:id', authenticatedOptions, async (req, res) => {
56
+ const { id } = req.params;
57
+ const { firstName, lastName, email, companyId } = req.body;
58
+ try {
59
+ const user = await UserExam.findById(id);
60
+ if (!user) {
61
+ return res.status(404).json({ message: 'no user founded with this id' });
62
+ }
63
+ const updateData = {
64
+ firstName: firstName || user.firstName,
65
+ lastName: lastName || user.lastName,
66
+ email: email || user.email,
67
+ companyId: companyId || user.companyId
68
+ };
69
+ await UserExam.findByIdAndUpdate(id, updateData, { new: true });
70
+ const updatedUser = await UserExam.findById(id);
71
+ res.status(200).json({ message: 'user updated', user: updatedUser });
72
+ }
73
+ catch (err) {
74
+ console.error('error when updating user : ', err);
75
+ res.status(500).json({ message: 'Internal server error' });
76
+ }
77
+ });
78
+ // Supprimer un utilisateur
79
+ this.delete('/:id', authenticatedOptions, async (req, res) => {
80
+ const { id } = req.params;
81
+ try {
82
+ const user = await UserExam.findByIdAndDelete(id);
83
+ if (!user) {
84
+ return res.status(404).json({ message: 'no user founded with this id' });
85
+ }
86
+ res.status(200).json({ message: 'user deleted', user });
87
+ }
88
+ catch (err) {
89
+ console.error('error when deleting user : ', err);
90
+ res.status(500).json({ message: 'Internal server error' });
91
+ }
92
+ });
93
+ }
94
+ }
95
+ const router = new UserRouter();
96
+ export default router;
@@ -0,0 +1,29 @@
1
+ export interface EdrmStorageConfig {
2
+ aws: {
3
+ region: string;
4
+ accessKeyId?: string;
5
+ secretAccessKey?: string;
6
+ bucket: string;
7
+ };
8
+ minio?: {
9
+ endpoint: string;
10
+ accessKey: string;
11
+ secretKey: string;
12
+ bucket: string;
13
+ useSSL: boolean;
14
+ };
15
+ general: {
16
+ defaultProvider: 'S3' | 'MINIO' | 'LOCAL';
17
+ maxFileSize: number;
18
+ allowedMimeTypes: string[];
19
+ uploadExpiresIn: number;
20
+ downloadExpiresIn: number;
21
+ };
22
+ events: {
23
+ enabled: boolean;
24
+ emitFileStored: boolean;
25
+ emitFileDeleted: boolean;
26
+ emitFileAccessed: boolean;
27
+ };
28
+ }
29
+ export declare const defaultConfig: EdrmStorageConfig;
@@ -0,0 +1,31 @@
1
+ export const defaultConfig = {
2
+ aws: {
3
+ region: process.env.AWS_REGION || 'us-east-1',
4
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
5
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
6
+ bucket: process.env.S3_BUCKET || 'edrm-storage'
7
+ },
8
+ general: {
9
+ defaultProvider: 'S3',
10
+ maxFileSize: 100 * 1024 * 1024, // 100MB
11
+ allowedMimeTypes: [
12
+ 'application/pdf',
13
+ 'application/msword',
14
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
15
+ 'image/jpeg',
16
+ 'image/png',
17
+ 'image/gif',
18
+ 'text/plain',
19
+ 'application/zip',
20
+ 'application/x-zip-compressed'
21
+ ],
22
+ uploadExpiresIn: 3600, // 1 heure
23
+ downloadExpiresIn: 3600 // 1 heure
24
+ },
25
+ events: {
26
+ enabled: true,
27
+ emitFileStored: true,
28
+ emitFileDeleted: true,
29
+ emitFileAccessed: true
30
+ }
31
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Configuration d'environnement pour le module EDRM Storage
3
+ * Copiez ce fichier vers .env et configurez vos valeurs
4
+ */
5
+ export declare const EdrmStorageEnvironment: {
6
+ AWS: {
7
+ REGION: string;
8
+ ACCESS_KEY_ID: string;
9
+ SECRET_ACCESS_KEY: string;
10
+ BUCKET: string;
11
+ };
12
+ MINIO: {
13
+ ENDPOINT: string;
14
+ ACCESS_KEY: string;
15
+ SECRET_KEY: string;
16
+ BUCKET: string;
17
+ USE_SSL: boolean;
18
+ };
19
+ GENERAL: {
20
+ DEFAULT_PROVIDER: string;
21
+ MAX_FILE_SIZE: number;
22
+ UPLOAD_EXPIRES_IN: number;
23
+ DOWNLOAD_EXPIRES_IN: number;
24
+ ALLOWED_MIME_TYPES: string[];
25
+ };
26
+ EVENTS: {
27
+ ENABLED: boolean;
28
+ EMIT_FILE_STORED: boolean;
29
+ EMIT_FILE_DELETED: boolean;
30
+ EMIT_FILE_ACCESSED: boolean;
31
+ };
32
+ SECURITY: {
33
+ PRESIGNED_URL_EXPIRES_IN: number;
34
+ MAX_CONCURRENT_UPLOADS: number;
35
+ ENABLE_ANTIVIRUS: boolean;
36
+ ENABLE_WATERMARKING: boolean;
37
+ };
38
+ MONITORING: {
39
+ ENABLE_METRICS: boolean;
40
+ LOG_LEVEL: string;
41
+ ENABLE_AUDIT_LOG: boolean;
42
+ };
43
+ };
44
+ /**
45
+ * Validation de la configuration
46
+ */
47
+ export declare function validateEdrmStorageConfig(): {
48
+ valid: boolean;
49
+ errors: string[];
50
+ };
51
+ /**
52
+ * Exemple de fichier .env
53
+ */
54
+ export declare const ENV_EXAMPLE = "\n# Configuration AWS S3\nAWS_REGION=us-east-1\nAWS_ACCESS_KEY_ID=your_access_key_here\nAWS_SECRET_ACCESS_KEY=your_secret_key_here\nS3_BUCKET=edrm-storage\n\n# Configuration MinIO (optionnel)\nMINIO_ENDPOINT=http://localhost:9000\nMINIO_ACCESS_KEY=your_minio_key\nMINIO_SECRET_KEY=your_minio_secret\nMINIO_BUCKET=edrm-storage\nMINIO_USE_SSL=false\n\n# Configuration g\u00E9n\u00E9rale\nEDRM_STORAGE_PROVIDER=S3\nEDRM_MAX_FILE_SIZE=104857600\nEDRM_UPLOAD_EXPIRES_IN=3600\nEDRM_DOWNLOAD_EXPIRES_IN=3600\nEDRM_ALLOWED_MIME_TYPES=application/pdf,image/jpeg,image/png\n\n# Configuration des \u00E9v\u00E9nements\nEDRM_EVENTS_ENABLED=true\nEDRM_EMIT_FILE_STORED=true\nEDRM_EMIT_FILE_DELETED=true\nEDRM_EMIT_FILE_ACCESSED=true\n\n# Configuration de s\u00E9curit\u00E9\nEDRM_PRESIGNED_URL_EXPIRES_IN=3600\nEDRM_MAX_CONCURRENT_UPLOADS=100\nEDRM_ENABLE_ANTIVIRUS=false\nEDRM_ENABLE_WATERMARKING=false\n\n# Configuration de monitoring\nEDRM_ENABLE_METRICS=true\nEDRM_LOG_LEVEL=info\nEDRM_ENABLE_AUDIT_LOG=false\n";