@programisto/edrm-storage 1.0.5 → 1.0.7
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/dist/modules/edrm-storage/models/file.model.d.ts +3 -0
- package/dist/modules/edrm-storage/models/file.model.js +6 -0
- package/dist/modules/edrm-storage/routes/edrm-storage.router.js +21 -9
- package/dist/modules/edrm-storage/services/edrm-storage.service.d.ts +6 -6
- package/dist/modules/edrm-storage/services/edrm-storage.service.js +21 -6
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ export interface IFile {
|
|
|
40
40
|
tenantId?: string;
|
|
41
41
|
entityName?: string;
|
|
42
42
|
entityId?: string;
|
|
43
|
+
portalEntityId?: string;
|
|
43
44
|
uploadedBy?: string;
|
|
44
45
|
expiresAt?: Date;
|
|
45
46
|
lastAccessedAt?: Date;
|
|
@@ -65,6 +66,8 @@ declare class File extends EnduranceSchema implements IFile {
|
|
|
65
66
|
tenantId: string;
|
|
66
67
|
entityName: string;
|
|
67
68
|
entityId: string;
|
|
69
|
+
/** Identifiant de l'entité du portail (multi-entités). Optionnel pour rétrocompatibilité. */
|
|
70
|
+
portalEntityId: string;
|
|
68
71
|
uploadedBy: string;
|
|
69
72
|
expiresAt: Date;
|
|
70
73
|
lastAccessedAt: Date;
|
|
@@ -56,6 +56,8 @@ let File = class File extends EnduranceSchema {
|
|
|
56
56
|
tenantId;
|
|
57
57
|
entityName;
|
|
58
58
|
entityId;
|
|
59
|
+
/** Identifiant de l'entité du portail (multi-entités). Optionnel pour rétrocompatibilité. */
|
|
60
|
+
portalEntityId;
|
|
59
61
|
uploadedBy;
|
|
60
62
|
expiresAt;
|
|
61
63
|
lastAccessedAt;
|
|
@@ -140,6 +142,10 @@ __decorate([
|
|
|
140
142
|
EnduranceModelType.prop({ required: false }),
|
|
141
143
|
__metadata("design:type", String)
|
|
142
144
|
], File.prototype, "entityId", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
EnduranceModelType.prop({ required: false }),
|
|
147
|
+
__metadata("design:type", String)
|
|
148
|
+
], File.prototype, "portalEntityId", void 0);
|
|
143
149
|
__decorate([
|
|
144
150
|
EnduranceModelType.prop({ required: false }),
|
|
145
151
|
__metadata("design:type", String)
|
|
@@ -47,6 +47,9 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
47
47
|
* entityId:
|
|
48
48
|
* type: string
|
|
49
49
|
* description: Identifiant de l’entité métier associée
|
|
50
|
+
* public:
|
|
51
|
+
* type: boolean
|
|
52
|
+
* description: Si true, image publique accessible depuis n'importe quelle entité (portalEntityId non défini)
|
|
50
53
|
* provider:
|
|
51
54
|
* type: string
|
|
52
55
|
* enum: [S3]
|
|
@@ -70,14 +73,23 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
70
73
|
*/
|
|
71
74
|
this.post('/files/init', userOptions, async (req, res) => {
|
|
72
75
|
try {
|
|
73
|
-
const { originalName, mimeType, size, tenantId, entityName, entityId, provider = 'S3', metadata, tags } = req.body;
|
|
74
|
-
|
|
76
|
+
const { originalName, mimeType, size, tenantId, entityName, entityId, public: isPublic = false, provider = 'S3', metadata, tags } = req.body;
|
|
77
|
+
const isPublicFile = isPublic === true;
|
|
78
|
+
const effectiveEntityName = isPublicFile ? 'portal' : entityName;
|
|
79
|
+
const effectiveEntityId = isPublicFile ? 'all' : entityId;
|
|
80
|
+
if (!originalName || !mimeType || !size || !tenantId) {
|
|
75
81
|
return res.status(400).json({
|
|
76
82
|
success: false,
|
|
77
|
-
message: 'Paramètres manquants: originalName, mimeType, size, tenantId
|
|
83
|
+
message: 'Paramètres manquants: originalName, mimeType, size, tenantId'
|
|
78
84
|
});
|
|
79
85
|
}
|
|
80
|
-
|
|
86
|
+
if (!isPublicFile && (!entityName || !entityId)) {
|
|
87
|
+
return res.status(400).json({
|
|
88
|
+
success: false,
|
|
89
|
+
message: 'Paramètres manquants: entityName et entityId (ou public: true)'
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const result = await this.storageService.initUpload(originalName, mimeType, size, tenantId, effectiveEntityName, effectiveEntityId, provider, metadata, tags, isPublicFile ? undefined : req.entity?._id?.toString());
|
|
81
93
|
return res.json({
|
|
82
94
|
success: true,
|
|
83
95
|
data: result
|
|
@@ -114,7 +126,7 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
114
126
|
this.post('/files/:fileId/complete', userOptions, async (req, res) => {
|
|
115
127
|
try {
|
|
116
128
|
const { fileId } = req.params;
|
|
117
|
-
const result = await this.storageService.completeUpload(fileId);
|
|
129
|
+
const result = await this.storageService.completeUpload(fileId, req.entity?._id?.toString());
|
|
118
130
|
return res.json({
|
|
119
131
|
success: true,
|
|
120
132
|
data: result
|
|
@@ -163,7 +175,7 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
163
175
|
try {
|
|
164
176
|
const { fileId } = req.params;
|
|
165
177
|
const { filename, expiresIn = 3600 } = req.query;
|
|
166
|
-
const result = await this.storageService.getDownloadUrl(fileId, filename, parseInt(expiresIn));
|
|
178
|
+
const result = await this.storageService.getDownloadUrl(fileId, filename, parseInt(expiresIn), req.entity?._id?.toString());
|
|
167
179
|
return res.json({
|
|
168
180
|
success: true,
|
|
169
181
|
data: result
|
|
@@ -200,7 +212,7 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
200
212
|
this.delete('/files/:fileId', adminOptions, async (req, res) => {
|
|
201
213
|
try {
|
|
202
214
|
const { fileId } = req.params;
|
|
203
|
-
await this.storageService.deleteFile(fileId);
|
|
215
|
+
await this.storageService.deleteFile(fileId, req.entity?._id?.toString());
|
|
204
216
|
return res.json({
|
|
205
217
|
success: true,
|
|
206
218
|
message: 'Fichier supprimé avec succès'
|
|
@@ -264,7 +276,7 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
264
276
|
this.get('/files', adminOptions, async (req, res) => {
|
|
265
277
|
try {
|
|
266
278
|
const { tenantId, entityName, entityId, status, page = 1, limit = 20 } = req.query;
|
|
267
|
-
const result = await this.storageService.listFiles(tenantId, entityName, entityId, status, parseInt(page), parseInt(limit));
|
|
279
|
+
const result = await this.storageService.listFiles(tenantId, entityName, entityId, status, parseInt(page), parseInt(limit), req.entity?._id?.toString());
|
|
268
280
|
return res.json({
|
|
269
281
|
success: true,
|
|
270
282
|
data: result
|
|
@@ -301,7 +313,7 @@ class EdrmStorageRouter extends EnduranceRouter {
|
|
|
301
313
|
this.get('/files/:fileId', userOptions, async (req, res) => {
|
|
302
314
|
try {
|
|
303
315
|
const { fileId } = req.params;
|
|
304
|
-
const result = await this.storageService.getFileById(fileId);
|
|
316
|
+
const result = await this.storageService.getFileById(fileId, req.entity?._id?.toString());
|
|
305
317
|
return res.json({
|
|
306
318
|
success: true,
|
|
307
319
|
data: result
|
|
@@ -7,7 +7,7 @@ export declare class EdrmStorageService {
|
|
|
7
7
|
private getProvider;
|
|
8
8
|
private generateKey;
|
|
9
9
|
private detectFileType;
|
|
10
|
-
initUpload(originalName: string, mimeType: string, size: number, tenantId: string, entityName: string, entityId: string, provider?: FileProvider, metadata?: Record<string, any>, tags?: string[]): Promise<{
|
|
10
|
+
initUpload(originalName: string, mimeType: string, size: number, tenantId: string, entityName: string, entityId: string, provider?: FileProvider, metadata?: Record<string, any>, tags?: string[], portalEntityId?: string): Promise<{
|
|
11
11
|
fileId: import("mongoose").Types.ObjectId;
|
|
12
12
|
uploadId: string;
|
|
13
13
|
presignedUrl: string;
|
|
@@ -15,14 +15,14 @@ export declare class EdrmStorageService {
|
|
|
15
15
|
bucket: string;
|
|
16
16
|
key: string;
|
|
17
17
|
}>;
|
|
18
|
-
completeUpload(fileId: string): Promise<any>;
|
|
19
|
-
getDownloadUrl(fileId: string, filename?: string, expiresIn?: number): Promise<any>;
|
|
20
|
-
deleteFile(fileId: string): Promise<void>;
|
|
21
|
-
listFiles(tenantId?: string, entityName?: string, entityId?: string, status?: FileStatus, page?: number, limit?: number): Promise<{
|
|
18
|
+
completeUpload(fileId: string, portalEntityId?: string): Promise<any>;
|
|
19
|
+
getDownloadUrl(fileId: string, filename?: string, expiresIn?: number, portalEntityId?: string): Promise<any>;
|
|
20
|
+
deleteFile(fileId: string, portalEntityId?: string): Promise<void>;
|
|
21
|
+
listFiles(tenantId?: string, entityName?: string, entityId?: string, status?: FileStatus, page?: number, limit?: number, portalEntityId?: string): Promise<{
|
|
22
22
|
files: any[];
|
|
23
23
|
total: number;
|
|
24
24
|
page: number;
|
|
25
25
|
totalPages: number;
|
|
26
26
|
}>;
|
|
27
|
-
getFileById(fileId: string): Promise<any>;
|
|
27
|
+
getFileById(fileId: string, portalEntityId?: string): Promise<any>;
|
|
28
28
|
}
|
|
@@ -41,7 +41,7 @@ export class EdrmStorageService {
|
|
|
41
41
|
return FileType.ARCHIVE;
|
|
42
42
|
return FileType.OTHER;
|
|
43
43
|
}
|
|
44
|
-
async initUpload(originalName, mimeType, size, tenantId, entityName, entityId, provider = this.defaultProvider, metadata, tags) {
|
|
44
|
+
async initUpload(originalName, mimeType, size, tenantId, entityName, entityId, provider = this.defaultProvider, metadata, tags, portalEntityId) {
|
|
45
45
|
const bucket = process.env.S3_BUCKET || 'edrm-storage';
|
|
46
46
|
const key = this.generateKey(tenantId, entityName, entityId, originalName);
|
|
47
47
|
const storageProvider = this.getProvider(provider);
|
|
@@ -64,6 +64,7 @@ export class EdrmStorageService {
|
|
|
64
64
|
tenantId,
|
|
65
65
|
entityName,
|
|
66
66
|
entityId,
|
|
67
|
+
...(portalEntityId && { portalEntityId }),
|
|
67
68
|
uploadedBy: 'system', // À remplacer par l'utilisateur connecté
|
|
68
69
|
accessCount: 0
|
|
69
70
|
});
|
|
@@ -77,11 +78,14 @@ export class EdrmStorageService {
|
|
|
77
78
|
key
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
|
-
async completeUpload(fileId) {
|
|
81
|
+
async completeUpload(fileId, portalEntityId) {
|
|
81
82
|
const fileRecord = await FileModel.findById(fileId);
|
|
82
83
|
if (!fileRecord) {
|
|
83
84
|
throw new Error('Fichier non trouvé');
|
|
84
85
|
}
|
|
86
|
+
if (portalEntityId && fileRecord.portalEntityId && fileRecord.portalEntityId !== portalEntityId) {
|
|
87
|
+
throw new Error('Fichier non trouvé');
|
|
88
|
+
}
|
|
85
89
|
const storageProvider = this.getProvider(fileRecord.provider);
|
|
86
90
|
try {
|
|
87
91
|
// Vérifier que le fichier existe dans le stockage
|
|
@@ -102,11 +106,14 @@ export class EdrmStorageService {
|
|
|
102
106
|
throw new Error(`Erreur lors de la finalisation de l'upload: ${error}`);
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
|
-
async getDownloadUrl(fileId, filename, expiresIn = 3600) {
|
|
109
|
+
async getDownloadUrl(fileId, filename, expiresIn = 3600, portalEntityId) {
|
|
106
110
|
const fileRecord = await FileModel.findById(fileId);
|
|
107
111
|
if (!fileRecord) {
|
|
108
112
|
throw new Error('Fichier non trouvé');
|
|
109
113
|
}
|
|
114
|
+
if (portalEntityId && fileRecord.portalEntityId && fileRecord.portalEntityId !== portalEntityId) {
|
|
115
|
+
throw new Error('Fichier non trouvé');
|
|
116
|
+
}
|
|
110
117
|
if (fileRecord.status !== FileStatus.COMPLETED) {
|
|
111
118
|
throw new Error('Fichier non encore finalisé');
|
|
112
119
|
}
|
|
@@ -123,11 +130,14 @@ export class EdrmStorageService {
|
|
|
123
130
|
contentType: downloadResponse.contentType
|
|
124
131
|
};
|
|
125
132
|
}
|
|
126
|
-
async deleteFile(fileId) {
|
|
133
|
+
async deleteFile(fileId, portalEntityId) {
|
|
127
134
|
const fileRecord = await FileModel.findById(fileId);
|
|
128
135
|
if (!fileRecord) {
|
|
129
136
|
throw new Error('Fichier non trouvé');
|
|
130
137
|
}
|
|
138
|
+
if (portalEntityId && fileRecord.portalEntityId && fileRecord.portalEntityId !== portalEntityId) {
|
|
139
|
+
throw new Error('Fichier non trouvé');
|
|
140
|
+
}
|
|
131
141
|
const storageProvider = this.getProvider(fileRecord.provider);
|
|
132
142
|
try {
|
|
133
143
|
// Supprimer du stockage
|
|
@@ -141,7 +151,7 @@ export class EdrmStorageService {
|
|
|
141
151
|
throw new Error(`Erreur lors de la suppression: ${error}`);
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
|
-
async listFiles(tenantId, entityName, entityId, status, page = 1, limit = 20) {
|
|
154
|
+
async listFiles(tenantId, entityName, entityId, status, page = 1, limit = 20, portalEntityId) {
|
|
145
155
|
const query = {};
|
|
146
156
|
if (tenantId)
|
|
147
157
|
query.tenantId = tenantId;
|
|
@@ -149,6 +159,8 @@ export class EdrmStorageService {
|
|
|
149
159
|
query.entityName = entityName;
|
|
150
160
|
if (entityId)
|
|
151
161
|
query.entityId = entityId;
|
|
162
|
+
if (portalEntityId)
|
|
163
|
+
query.portalEntityId = portalEntityId;
|
|
152
164
|
if (status)
|
|
153
165
|
query.status = status;
|
|
154
166
|
const skip = (page - 1) * limit;
|
|
@@ -167,11 +179,14 @@ export class EdrmStorageService {
|
|
|
167
179
|
totalPages: Math.ceil(total / limit)
|
|
168
180
|
};
|
|
169
181
|
}
|
|
170
|
-
async getFileById(fileId) {
|
|
182
|
+
async getFileById(fileId, portalEntityId) {
|
|
171
183
|
const fileRecord = await FileModel.findById(fileId);
|
|
172
184
|
if (!fileRecord) {
|
|
173
185
|
throw new Error('Fichier non trouvé');
|
|
174
186
|
}
|
|
187
|
+
if (portalEntityId && fileRecord.portalEntityId && fileRecord.portalEntityId !== portalEntityId) {
|
|
188
|
+
throw new Error('Fichier non trouvé');
|
|
189
|
+
}
|
|
175
190
|
return fileRecord;
|
|
176
191
|
}
|
|
177
192
|
}
|