@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.
- package/README.md +135 -0
- package/dist/bin/www.d.ts +2 -0
- package/dist/bin/www.js +13 -0
- package/dist/modules/edrm-exams/lib/openai/correctQuestion.txt +9 -0
- package/dist/modules/edrm-exams/lib/openai/createQuestion.txt +6 -0
- package/dist/modules/edrm-exams/lib/openai.d.ts +37 -0
- package/dist/modules/edrm-exams/lib/openai.js +135 -0
- package/dist/modules/edrm-exams/listeners/correct.listener.d.ts +2 -0
- package/dist/modules/edrm-exams/listeners/correct.listener.js +167 -0
- package/dist/modules/edrm-exams/models/candidate.model.d.ts +21 -0
- package/dist/modules/edrm-exams/models/candidate.model.js +75 -0
- package/dist/modules/edrm-exams/models/candidate.models.d.ts +21 -0
- package/dist/modules/edrm-exams/models/candidate.models.js +75 -0
- package/dist/modules/edrm-exams/models/company.model.d.ts +8 -0
- package/dist/modules/edrm-exams/models/company.model.js +34 -0
- package/dist/modules/edrm-exams/models/contact.model.d.ts +14 -0
- package/dist/modules/edrm-exams/models/contact.model.js +60 -0
- package/dist/modules/edrm-exams/models/test-category.models.d.ts +7 -0
- package/dist/modules/edrm-exams/models/test-category.models.js +29 -0
- package/dist/modules/edrm-exams/models/test-job.model.d.ts +7 -0
- package/dist/modules/edrm-exams/models/test-job.model.js +29 -0
- package/dist/modules/edrm-exams/models/test-question.model.d.ts +25 -0
- package/dist/modules/edrm-exams/models/test-question.model.js +70 -0
- package/dist/modules/edrm-exams/models/test-result.model.d.ts +26 -0
- package/dist/modules/edrm-exams/models/test-result.model.js +70 -0
- package/dist/modules/edrm-exams/models/test.model.d.ts +47 -0
- package/dist/modules/edrm-exams/models/test.model.js +133 -0
- package/dist/modules/edrm-exams/models/user.model.d.ts +18 -0
- package/dist/modules/edrm-exams/models/user.model.js +73 -0
- package/dist/modules/edrm-exams/routes/company.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/company.router.js +108 -0
- package/dist/modules/edrm-exams/routes/exams-candidate.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/exams-candidate.router.js +448 -0
- package/dist/modules/edrm-exams/routes/exams.router.d.ts +8 -0
- package/dist/modules/edrm-exams/routes/exams.router.js +1343 -0
- package/dist/modules/edrm-exams/routes/result.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/result.router.js +370 -0
- package/dist/modules/edrm-exams/routes/user.router.d.ts +7 -0
- package/dist/modules/edrm-exams/routes/user.router.js +96 -0
- package/dist/modules/edrm-storage/config/edrm-storage.config.d.ts +29 -0
- package/dist/modules/edrm-storage/config/edrm-storage.config.js +31 -0
- package/dist/modules/edrm-storage/config/environment.example.d.ts +54 -0
- package/dist/modules/edrm-storage/config/environment.example.js +130 -0
- package/dist/modules/edrm-storage/examples/usage.example.d.ts +52 -0
- package/dist/modules/edrm-storage/examples/usage.example.js +156 -0
- package/dist/modules/edrm-storage/index.d.ts +5 -0
- package/dist/modules/edrm-storage/index.js +8 -0
- package/dist/modules/edrm-storage/integration/edrm-storage-integration.d.ts +53 -0
- package/dist/modules/edrm-storage/integration/edrm-storage-integration.js +132 -0
- package/dist/modules/edrm-storage/interfaces/storage-provider.interface.d.ts +35 -0
- package/dist/modules/edrm-storage/interfaces/storage-provider.interface.js +1 -0
- package/dist/modules/edrm-storage/migrations/edrm-storage.migration.d.ts +6 -0
- package/dist/modules/edrm-storage/migrations/edrm-storage.migration.js +151 -0
- package/dist/modules/edrm-storage/models/file.model.d.ts +78 -0
- package/dist/modules/edrm-storage/models/file.model.js +190 -0
- package/dist/modules/edrm-storage/providers/s3-storage.provider.d.ts +18 -0
- package/dist/modules/edrm-storage/providers/s3-storage.provider.js +95 -0
- package/dist/modules/edrm-storage/routes/edrm-storage.router.d.ts +8 -0
- package/dist/modules/edrm-storage/routes/edrm-storage.router.js +155 -0
- package/dist/modules/edrm-storage/scripts/quick-start.d.ts +7 -0
- package/dist/modules/edrm-storage/scripts/quick-start.js +114 -0
- package/dist/modules/edrm-storage/services/edrm-storage.service.d.ts +29 -0
- package/dist/modules/edrm-storage/services/edrm-storage.service.js +188 -0
- package/dist/modules/edrm-storage/tests/edrm-storage.service.test.d.ts +1 -0
- package/dist/modules/edrm-storage/tests/edrm-storage.service.test.js +143 -0
- package/dist/modules/edrm-storage/tests/integration.test.d.ts +1 -0
- package/dist/modules/edrm-storage/tests/integration.test.js +141 -0
- package/package.json +81 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Migration pour créer la collection files du module edrm-storage
|
|
2
|
+
// Ce fichier peut être exécuté pour initialiser la base de données
|
|
3
|
+
import mongoose from 'mongoose';
|
|
4
|
+
export async function createFilesCollection() {
|
|
5
|
+
try {
|
|
6
|
+
// Vérifier si la connexion MongoDB est établie
|
|
7
|
+
if (!mongoose.connection.db) {
|
|
8
|
+
throw new Error('Connexion MongoDB non établie');
|
|
9
|
+
}
|
|
10
|
+
// Vérifier si la collection existe déjà
|
|
11
|
+
const collections = await mongoose.connection.db.listCollections().toArray();
|
|
12
|
+
const filesCollectionExists = collections.some(col => col.name === 'files');
|
|
13
|
+
if (!filesCollectionExists) {
|
|
14
|
+
// Créer la collection avec des index optimisés
|
|
15
|
+
await mongoose.connection.db.createCollection('files');
|
|
16
|
+
// Créer les index pour optimiser les requêtes
|
|
17
|
+
await mongoose.connection.db.collection('files').createIndexes([
|
|
18
|
+
// Index sur tenantId pour l'isolation multi-tenant
|
|
19
|
+
{ key: { tenantId: 1 } },
|
|
20
|
+
// Index sur entityName et entityId pour les requêtes par entité
|
|
21
|
+
{ key: { entityName: 1, entityId: 1 } },
|
|
22
|
+
// Index sur status pour filtrer par statut
|
|
23
|
+
{ key: { status: 1 } },
|
|
24
|
+
// Index sur provider pour filtrer par provider
|
|
25
|
+
{ key: { provider: 1 } },
|
|
26
|
+
// Index sur uploadedBy pour les requêtes par utilisateur
|
|
27
|
+
{ key: { uploadedBy: 1 } },
|
|
28
|
+
// Index sur createdAt pour le tri chronologique
|
|
29
|
+
{ key: { createdAt: -1 } },
|
|
30
|
+
// Index sur lastAccessedAt pour les statistiques
|
|
31
|
+
{ key: { lastAccessedAt: -1 } },
|
|
32
|
+
// Index composé pour les requêtes fréquentes
|
|
33
|
+
{ key: { tenantId: 1, entityName: 1, entityId: 1, status: 1 } },
|
|
34
|
+
// Index sur les tags pour la recherche
|
|
35
|
+
{ key: { tags: 1 } },
|
|
36
|
+
// Index sur le type de fichier
|
|
37
|
+
{ key: { type: 1 } },
|
|
38
|
+
// Index sur expiresAt pour le nettoyage automatique
|
|
39
|
+
{ key: { expiresAt: 1 } }
|
|
40
|
+
]);
|
|
41
|
+
console.log('Collection "files" créée avec succès avec tous les index');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log('Collection "files" existe déjà');
|
|
45
|
+
}
|
|
46
|
+
// Créer des index supplémentaires si nécessaire
|
|
47
|
+
if (mongoose.connection.db) {
|
|
48
|
+
await mongoose.connection.db.collection('files').createIndexes([
|
|
49
|
+
// Index textuel pour la recherche dans les métadonnées
|
|
50
|
+
{
|
|
51
|
+
key: {
|
|
52
|
+
originalName: 'text',
|
|
53
|
+
description: 'text'
|
|
54
|
+
},
|
|
55
|
+
name: 'text_search_index'
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
console.log('Migration edrm-storage terminée avec succès');
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('Erreur lors de la migration edrm-storage:', error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Fonction pour nettoyer les fichiers expirés
|
|
67
|
+
export async function cleanupExpiredFiles() {
|
|
68
|
+
try {
|
|
69
|
+
if (!mongoose.connection.db) {
|
|
70
|
+
throw new Error('Connexion MongoDB non établie');
|
|
71
|
+
}
|
|
72
|
+
const result = await mongoose.connection.db.collection('files').deleteMany({
|
|
73
|
+
expiresAt: { $lt: new Date() },
|
|
74
|
+
status: { $in: ['PENDING', 'FAILED'] }
|
|
75
|
+
});
|
|
76
|
+
console.log(`${result.deletedCount} fichiers expirés supprimés`);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error('Erreur lors du nettoyage des fichiers expirés:', error);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Fonction pour créer des statistiques
|
|
84
|
+
export async function createFileStatistics() {
|
|
85
|
+
try {
|
|
86
|
+
if (!mongoose.connection.db) {
|
|
87
|
+
throw new Error('Connexion MongoDB non établie');
|
|
88
|
+
}
|
|
89
|
+
const stats = await mongoose.connection.db.collection('files').aggregate([
|
|
90
|
+
{
|
|
91
|
+
$group: {
|
|
92
|
+
_id: null,
|
|
93
|
+
totalFiles: { $sum: 1 },
|
|
94
|
+
totalSize: { $sum: '$size' },
|
|
95
|
+
avgFileSize: { $avg: '$size' },
|
|
96
|
+
filesByStatus: {
|
|
97
|
+
$push: {
|
|
98
|
+
status: '$status',
|
|
99
|
+
count: 1
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
filesByProvider: {
|
|
103
|
+
$push: {
|
|
104
|
+
provider: '$provider',
|
|
105
|
+
count: 1
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
filesByType: {
|
|
109
|
+
$push: {
|
|
110
|
+
type: '$type',
|
|
111
|
+
count: 1
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
]).toArray();
|
|
117
|
+
console.log('Statistiques des fichiers:', stats[0]);
|
|
118
|
+
return stats[0];
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error('Erreur lors de la création des statistiques:', error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Fonction pour migrer les données existantes (si nécessaire)
|
|
126
|
+
export async function migrateExistingFiles() {
|
|
127
|
+
try {
|
|
128
|
+
// Cette fonction peut être utilisée pour migrer des fichiers existants
|
|
129
|
+
// vers le nouveau système edrm-storage
|
|
130
|
+
console.log('Migration des fichiers existants terminée');
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error('Erreur lors de la migration des fichiers existants:', error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Fonction principale pour exécuter toutes les migrations
|
|
138
|
+
export async function runEdrmStorageMigrations() {
|
|
139
|
+
try {
|
|
140
|
+
console.log('Début des migrations edrm-storage...');
|
|
141
|
+
await createFilesCollection();
|
|
142
|
+
await cleanupExpiredFiles();
|
|
143
|
+
await createFileStatistics();
|
|
144
|
+
await migrateExistingFiles();
|
|
145
|
+
console.log('Toutes les migrations edrm-storage terminées avec succès');
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error('Erreur lors des migrations edrm-storage:', error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EnduranceSchema } from '@programisto/endurance-core';
|
|
2
|
+
export declare enum FileStatus {
|
|
3
|
+
PENDING = "PENDING",
|
|
4
|
+
UPLOADING = "UPLOADING",
|
|
5
|
+
COMPLETED = "COMPLETED",
|
|
6
|
+
FAILED = "FAILED",
|
|
7
|
+
DELETED = "DELETED"
|
|
8
|
+
}
|
|
9
|
+
export declare enum FileProvider {
|
|
10
|
+
S3 = "S3",
|
|
11
|
+
MINIO = "MINIO",
|
|
12
|
+
LOCAL = "LOCAL",
|
|
13
|
+
GCS = "GCS"
|
|
14
|
+
}
|
|
15
|
+
export declare enum FileType {
|
|
16
|
+
DOCUMENT = "DOCUMENT",
|
|
17
|
+
IMAGE = "IMAGE",
|
|
18
|
+
VIDEO = "VIDEO",
|
|
19
|
+
AUDIO = "AUDIO",
|
|
20
|
+
ARCHIVE = "ARCHIVE",
|
|
21
|
+
OTHER = "OTHER"
|
|
22
|
+
}
|
|
23
|
+
export interface IFile {
|
|
24
|
+
filename: string;
|
|
25
|
+
originalName: string;
|
|
26
|
+
mimeType: string;
|
|
27
|
+
size: number;
|
|
28
|
+
provider: FileProvider;
|
|
29
|
+
bucket: string;
|
|
30
|
+
key: string;
|
|
31
|
+
url: string;
|
|
32
|
+
status: FileStatus;
|
|
33
|
+
type?: FileType;
|
|
34
|
+
metadata?: Record<string, any>;
|
|
35
|
+
tags?: string[];
|
|
36
|
+
description?: string;
|
|
37
|
+
checksum?: string;
|
|
38
|
+
etag?: string;
|
|
39
|
+
versionId?: string;
|
|
40
|
+
tenantId?: string;
|
|
41
|
+
entityName?: string;
|
|
42
|
+
entityId?: string;
|
|
43
|
+
uploadedBy?: string;
|
|
44
|
+
expiresAt?: Date;
|
|
45
|
+
lastAccessedAt?: Date;
|
|
46
|
+
accessCount?: number;
|
|
47
|
+
}
|
|
48
|
+
declare class File extends EnduranceSchema implements IFile {
|
|
49
|
+
filename: string;
|
|
50
|
+
originalName: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
size: number;
|
|
53
|
+
provider: FileProvider;
|
|
54
|
+
bucket: string;
|
|
55
|
+
key: string;
|
|
56
|
+
url: string;
|
|
57
|
+
status: FileStatus;
|
|
58
|
+
type: FileType;
|
|
59
|
+
metadata: Record<string, any>;
|
|
60
|
+
tags: string[];
|
|
61
|
+
description: string;
|
|
62
|
+
checksum: string;
|
|
63
|
+
etag: string;
|
|
64
|
+
versionId: string;
|
|
65
|
+
tenantId: string;
|
|
66
|
+
entityName: string;
|
|
67
|
+
entityId: string;
|
|
68
|
+
uploadedBy: string;
|
|
69
|
+
expiresAt: Date;
|
|
70
|
+
lastAccessedAt: Date;
|
|
71
|
+
accessCount: number;
|
|
72
|
+
static getModel(): import("@typegoose/typegoose").ReturnModelType<typeof File, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
73
|
+
}
|
|
74
|
+
declare const FileModel: import("@typegoose/typegoose").ReturnModelType<typeof File, import("@typegoose/typegoose/lib/types").BeAnObject>;
|
|
75
|
+
export declare const FileStatusValues: FileStatus[];
|
|
76
|
+
export declare const FileProviderValues: FileProvider[];
|
|
77
|
+
export declare const FileTypeValues: FileType[];
|
|
78
|
+
export default FileModel;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { EnduranceSchema, EnduranceModelType } from '@programisto/endurance-core';
|
|
11
|
+
// Enums pour les statuts de fichiers
|
|
12
|
+
export var FileStatus;
|
|
13
|
+
(function (FileStatus) {
|
|
14
|
+
FileStatus["PENDING"] = "PENDING";
|
|
15
|
+
FileStatus["UPLOADING"] = "UPLOADING";
|
|
16
|
+
FileStatus["COMPLETED"] = "COMPLETED";
|
|
17
|
+
FileStatus["FAILED"] = "FAILED";
|
|
18
|
+
FileStatus["DELETED"] = "DELETED";
|
|
19
|
+
})(FileStatus || (FileStatus = {}));
|
|
20
|
+
// Enums pour les providers de stockage
|
|
21
|
+
export var FileProvider;
|
|
22
|
+
(function (FileProvider) {
|
|
23
|
+
FileProvider["S3"] = "S3";
|
|
24
|
+
FileProvider["MINIO"] = "MINIO";
|
|
25
|
+
FileProvider["LOCAL"] = "LOCAL";
|
|
26
|
+
FileProvider["GCS"] = "GCS";
|
|
27
|
+
})(FileProvider || (FileProvider = {}));
|
|
28
|
+
// Enums pour les types de fichiers
|
|
29
|
+
export var FileType;
|
|
30
|
+
(function (FileType) {
|
|
31
|
+
FileType["DOCUMENT"] = "DOCUMENT";
|
|
32
|
+
FileType["IMAGE"] = "IMAGE";
|
|
33
|
+
FileType["VIDEO"] = "VIDEO";
|
|
34
|
+
FileType["AUDIO"] = "AUDIO";
|
|
35
|
+
FileType["ARCHIVE"] = "ARCHIVE";
|
|
36
|
+
FileType["OTHER"] = "OTHER";
|
|
37
|
+
})(FileType || (FileType = {}));
|
|
38
|
+
let File = class File extends EnduranceSchema {
|
|
39
|
+
filename;
|
|
40
|
+
originalName;
|
|
41
|
+
mimeType;
|
|
42
|
+
size;
|
|
43
|
+
provider;
|
|
44
|
+
bucket;
|
|
45
|
+
key;
|
|
46
|
+
url;
|
|
47
|
+
status;
|
|
48
|
+
type;
|
|
49
|
+
metadata;
|
|
50
|
+
tags;
|
|
51
|
+
description;
|
|
52
|
+
checksum;
|
|
53
|
+
etag;
|
|
54
|
+
versionId;
|
|
55
|
+
tenantId;
|
|
56
|
+
entityName;
|
|
57
|
+
entityId;
|
|
58
|
+
uploadedBy;
|
|
59
|
+
expiresAt;
|
|
60
|
+
lastAccessedAt;
|
|
61
|
+
accessCount;
|
|
62
|
+
static getModel() {
|
|
63
|
+
return FileModel;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
__decorate([
|
|
67
|
+
EnduranceModelType.prop({ required: true }),
|
|
68
|
+
__metadata("design:type", String)
|
|
69
|
+
], File.prototype, "filename", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
EnduranceModelType.prop({ required: true }),
|
|
72
|
+
__metadata("design:type", String)
|
|
73
|
+
], File.prototype, "originalName", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
EnduranceModelType.prop({ required: true }),
|
|
76
|
+
__metadata("design:type", String)
|
|
77
|
+
], File.prototype, "mimeType", void 0);
|
|
78
|
+
__decorate([
|
|
79
|
+
EnduranceModelType.prop({ required: true, type: Number }),
|
|
80
|
+
__metadata("design:type", Number)
|
|
81
|
+
], File.prototype, "size", void 0);
|
|
82
|
+
__decorate([
|
|
83
|
+
EnduranceModelType.prop({ required: true, enum: FileProvider }),
|
|
84
|
+
__metadata("design:type", String)
|
|
85
|
+
], File.prototype, "provider", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
EnduranceModelType.prop({ required: true }),
|
|
88
|
+
__metadata("design:type", String)
|
|
89
|
+
], File.prototype, "bucket", void 0);
|
|
90
|
+
__decorate([
|
|
91
|
+
EnduranceModelType.prop({ required: true }),
|
|
92
|
+
__metadata("design:type", String)
|
|
93
|
+
], File.prototype, "key", void 0);
|
|
94
|
+
__decorate([
|
|
95
|
+
EnduranceModelType.prop({ required: true }),
|
|
96
|
+
__metadata("design:type", String)
|
|
97
|
+
], File.prototype, "url", void 0);
|
|
98
|
+
__decorate([
|
|
99
|
+
EnduranceModelType.prop({ required: true, enum: FileStatus, default: FileStatus.PENDING }),
|
|
100
|
+
__metadata("design:type", String)
|
|
101
|
+
], File.prototype, "status", void 0);
|
|
102
|
+
__decorate([
|
|
103
|
+
EnduranceModelType.prop({ required: false, enum: FileType }),
|
|
104
|
+
__metadata("design:type", String)
|
|
105
|
+
], File.prototype, "type", void 0);
|
|
106
|
+
__decorate([
|
|
107
|
+
EnduranceModelType.prop({ required: false }),
|
|
108
|
+
__metadata("design:type", Object)
|
|
109
|
+
], File.prototype, "metadata", void 0);
|
|
110
|
+
__decorate([
|
|
111
|
+
EnduranceModelType.prop({ required: false }),
|
|
112
|
+
__metadata("design:type", Array)
|
|
113
|
+
], File.prototype, "tags", void 0);
|
|
114
|
+
__decorate([
|
|
115
|
+
EnduranceModelType.prop({ required: false }),
|
|
116
|
+
__metadata("design:type", String)
|
|
117
|
+
], File.prototype, "description", void 0);
|
|
118
|
+
__decorate([
|
|
119
|
+
EnduranceModelType.prop({ required: false }),
|
|
120
|
+
__metadata("design:type", String)
|
|
121
|
+
], File.prototype, "checksum", void 0);
|
|
122
|
+
__decorate([
|
|
123
|
+
EnduranceModelType.prop({ required: false }),
|
|
124
|
+
__metadata("design:type", String)
|
|
125
|
+
], File.prototype, "etag", void 0);
|
|
126
|
+
__decorate([
|
|
127
|
+
EnduranceModelType.prop({ required: false }),
|
|
128
|
+
__metadata("design:type", String)
|
|
129
|
+
], File.prototype, "versionId", void 0);
|
|
130
|
+
__decorate([
|
|
131
|
+
EnduranceModelType.prop({ required: false }),
|
|
132
|
+
__metadata("design:type", String)
|
|
133
|
+
], File.prototype, "tenantId", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
EnduranceModelType.prop({ required: false }),
|
|
136
|
+
__metadata("design:type", String)
|
|
137
|
+
], File.prototype, "entityName", void 0);
|
|
138
|
+
__decorate([
|
|
139
|
+
EnduranceModelType.prop({ required: false }),
|
|
140
|
+
__metadata("design:type", String)
|
|
141
|
+
], File.prototype, "entityId", void 0);
|
|
142
|
+
__decorate([
|
|
143
|
+
EnduranceModelType.prop({ required: false }),
|
|
144
|
+
__metadata("design:type", String)
|
|
145
|
+
], File.prototype, "uploadedBy", void 0);
|
|
146
|
+
__decorate([
|
|
147
|
+
EnduranceModelType.prop({ required: false }),
|
|
148
|
+
__metadata("design:type", Date)
|
|
149
|
+
], File.prototype, "expiresAt", void 0);
|
|
150
|
+
__decorate([
|
|
151
|
+
EnduranceModelType.prop({ required: false }),
|
|
152
|
+
__metadata("design:type", Date)
|
|
153
|
+
], File.prototype, "lastAccessedAt", void 0);
|
|
154
|
+
__decorate([
|
|
155
|
+
EnduranceModelType.prop({ required: false }),
|
|
156
|
+
__metadata("design:type", Number)
|
|
157
|
+
], File.prototype, "accessCount", void 0);
|
|
158
|
+
File = __decorate([
|
|
159
|
+
EnduranceModelType.modelOptions({
|
|
160
|
+
options: {
|
|
161
|
+
allowMixed: EnduranceModelType.Severity.ALLOW
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
165
|
+
,
|
|
166
|
+
EnduranceModelType.pre('save', async function (next) {
|
|
167
|
+
try {
|
|
168
|
+
if (this.status) {
|
|
169
|
+
this.status = this.status.toUpperCase();
|
|
170
|
+
}
|
|
171
|
+
if (this.provider) {
|
|
172
|
+
this.provider = this.provider.toUpperCase();
|
|
173
|
+
}
|
|
174
|
+
if (this.type) {
|
|
175
|
+
this.type = this.type.toUpperCase();
|
|
176
|
+
}
|
|
177
|
+
next();
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
181
|
+
next(new Error('Erreur lors du pré-enregistrement: ' + errorMessage));
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
], File);
|
|
185
|
+
const FileModel = EnduranceModelType.getModelForClass(File);
|
|
186
|
+
// Exports explicites pour éviter les warnings ESLint sur les enums non utilisés
|
|
187
|
+
export const FileStatusValues = Object.values(FileStatus);
|
|
188
|
+
export const FileProviderValues = Object.values(FileProvider);
|
|
189
|
+
export const FileTypeValues = Object.values(FileType);
|
|
190
|
+
export default FileModel;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StorageProvider, UploadInitResponse, DownloadUrlResponse, FileMetadata } from '../interfaces/storage-provider.interface.js';
|
|
2
|
+
import { FileProvider } from '../models/file.model.js';
|
|
3
|
+
export declare class S3StorageProvider implements StorageProvider {
|
|
4
|
+
provider: FileProvider;
|
|
5
|
+
private client;
|
|
6
|
+
private region;
|
|
7
|
+
constructor(region: string, accessKeyId?: string, secretAccessKey?: string);
|
|
8
|
+
initUpload(bucket: string, key: string, contentType: string, expiresIn?: number): Promise<UploadInitResponse>;
|
|
9
|
+
getFileMetadata(bucket: string, key: string): Promise<FileMetadata>;
|
|
10
|
+
getDownloadUrl(bucket: string, key: string, filename?: string, expiresIn?: number): Promise<DownloadUrlResponse>;
|
|
11
|
+
deleteFile(bucket: string, key: string): Promise<void>;
|
|
12
|
+
copyFile(sourceBucket: string, sourceKey: string, destBucket: string, destKey: string): Promise<void>;
|
|
13
|
+
listFiles(bucket: string, prefix?: string, maxKeys?: number): Promise<{
|
|
14
|
+
key: string;
|
|
15
|
+
size: number;
|
|
16
|
+
lastModified: Date;
|
|
17
|
+
}[]>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, HeadObjectCommand, CopyObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
2
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
|
+
import { FileProvider } from '../models/file.model.js';
|
|
4
|
+
export class S3StorageProvider {
|
|
5
|
+
provider = FileProvider.S3;
|
|
6
|
+
client;
|
|
7
|
+
region;
|
|
8
|
+
constructor(region, accessKeyId, secretAccessKey) {
|
|
9
|
+
this.region = region;
|
|
10
|
+
this.client = new S3Client({
|
|
11
|
+
region: this.region,
|
|
12
|
+
credentials: accessKeyId && secretAccessKey
|
|
13
|
+
? {
|
|
14
|
+
accessKeyId,
|
|
15
|
+
secretAccessKey
|
|
16
|
+
}
|
|
17
|
+
: undefined
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async initUpload(bucket, key, contentType, expiresIn = 3600) {
|
|
21
|
+
const command = new PutObjectCommand({
|
|
22
|
+
Bucket: bucket,
|
|
23
|
+
Key: key,
|
|
24
|
+
ContentType: contentType
|
|
25
|
+
});
|
|
26
|
+
const presignedUrl = await getSignedUrl(this.client, command, {
|
|
27
|
+
expiresIn
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
uploadId: `${bucket}-${key}-${Date.now()}`,
|
|
31
|
+
presignedUrl,
|
|
32
|
+
expiresAt: new Date(Date.now() + expiresIn * 1000),
|
|
33
|
+
bucket,
|
|
34
|
+
key
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async getFileMetadata(bucket, key) {
|
|
38
|
+
const command = new HeadObjectCommand({
|
|
39
|
+
Bucket: bucket,
|
|
40
|
+
Key: key
|
|
41
|
+
});
|
|
42
|
+
const response = await this.client.send(command);
|
|
43
|
+
return {
|
|
44
|
+
size: response.ContentLength || 0,
|
|
45
|
+
etag: response.ETag?.replace(/"/g, '') || '',
|
|
46
|
+
lastModified: response.LastModified || new Date(),
|
|
47
|
+
contentType: response.ContentType || 'application/octet-stream',
|
|
48
|
+
metadata: response.Metadata
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async getDownloadUrl(bucket, key, filename, expiresIn = 3600) {
|
|
52
|
+
const command = new GetObjectCommand({
|
|
53
|
+
Bucket: bucket,
|
|
54
|
+
Key: key,
|
|
55
|
+
ResponseContentDisposition: filename ? `attachment; filename="${filename}"` : undefined
|
|
56
|
+
});
|
|
57
|
+
const url = await getSignedUrl(this.client, command, {
|
|
58
|
+
expiresIn
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
url,
|
|
62
|
+
expiresAt: new Date(Date.now() + expiresIn * 1000),
|
|
63
|
+
filename: filename || key.split('/').pop() || 'file',
|
|
64
|
+
contentType: 'application/octet-stream'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async deleteFile(bucket, key) {
|
|
68
|
+
const command = new DeleteObjectCommand({
|
|
69
|
+
Bucket: bucket,
|
|
70
|
+
Key: key
|
|
71
|
+
});
|
|
72
|
+
await this.client.send(command);
|
|
73
|
+
}
|
|
74
|
+
async copyFile(sourceBucket, sourceKey, destBucket, destKey) {
|
|
75
|
+
const command = new CopyObjectCommand({
|
|
76
|
+
CopySource: `${sourceBucket}/${sourceKey}`,
|
|
77
|
+
Bucket: destBucket,
|
|
78
|
+
Key: destKey
|
|
79
|
+
});
|
|
80
|
+
await this.client.send(command);
|
|
81
|
+
}
|
|
82
|
+
async listFiles(bucket, prefix, maxKeys = 1000) {
|
|
83
|
+
const command = new ListObjectsV2Command({
|
|
84
|
+
Bucket: bucket,
|
|
85
|
+
Prefix: prefix,
|
|
86
|
+
MaxKeys: maxKeys
|
|
87
|
+
});
|
|
88
|
+
const response = await this.client.send(command);
|
|
89
|
+
return (response.Contents || []).map(obj => ({
|
|
90
|
+
key: obj.Key || '',
|
|
91
|
+
size: obj.Size || 0,
|
|
92
|
+
lastModified: obj.LastModified || new Date()
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
}
|