@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,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
+ }
@@ -0,0 +1,8 @@
1
+ import { EnduranceRouter } from '@programisto/endurance-core';
2
+ declare class EdrmStorageRouter extends EnduranceRouter {
3
+ private storageService;
4
+ constructor();
5
+ setupRoutes(): void;
6
+ }
7
+ declare const router: EdrmStorageRouter;
8
+ export default router;