@tomei/media 0.8.8 → 0.8.9
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/package.json +1 -1
- package/src/base/base.medias.ts +210 -210
- package/src/base/base.repository.abstract.ts +28 -28
- package/src/base/base.repository.interface.ts +9 -9
- package/src/common/common.module.ts +10 -10
- package/src/common/common.service.ts +107 -107
- package/src/common/dto/add-field-translation.dto.ts +23 -23
- package/src/common/dto/get-media.dto.ts +12 -12
- package/src/database.ts +16 -16
- package/src/dto/external-media.dto.ts +49 -49
- package/src/dto/internal-medias.dto.ts +64 -64
- package/src/entities/medias.entity.ts +125 -125
- package/src/enum/index.ts +3 -3
- package/src/enum/medias.enum.ts +5 -5
- package/src/helpers/error.ts +12 -12
- package/src/helpers/index.ts +3 -3
- package/src/index.ts +24 -24
- package/src/interfaces/base.medias-attr.interface.ts +21 -21
- package/src/interfaces/medias.repository.interface.ts +4 -4
- package/src/medias.repository.ts +55 -55
- package/src/medias.ts +769 -769
- package/src/pipe/append-id.pipe.ts +15 -15
- package/src/pipe/validate-enum.pipe.ts +31 -31
- package/src/pipe/validate-id.pipe.ts +18 -18
- package/src/pipe/validate-search.pipe.ts +44 -44
- package/src/responses/deleted.response.ts +6 -6
- package/src/responses/pagination.response.ts +10 -10
- package/dist/base/base.medias.d.ts +0 -38
- package/dist/base/base.medias.js +0 -175
- package/dist/base/base.medias.js.map +0 -1
- package/dist/base/base.repository.abstract.d.ts +0 -12
- package/dist/base/base.repository.abstract.js +0 -22
- package/dist/base/base.repository.abstract.js.map +0 -1
- package/dist/base/base.repository.interface.d.ts +0 -9
- package/dist/base/base.repository.interface.js +0 -3
- package/dist/base/base.repository.interface.js.map +0 -1
- package/dist/common/common.module.d.ts +0 -2
- package/dist/common/common.module.js +0 -23
- package/dist/common/common.module.js.map +0 -1
- package/dist/common/common.service.d.ts +0 -13
- package/dist/common/common.service.js +0 -106
- package/dist/common/common.service.js.map +0 -1
- package/dist/common/dto/add-field-translation.dto.d.ts +0 -7
- package/dist/common/dto/add-field-translation.dto.js +0 -42
- package/dist/common/dto/add-field-translation.dto.js.map +0 -1
- package/dist/common/dto/get-media.dto.d.ts +0 -5
- package/dist/common/dto/get-media.dto.js +0 -29
- package/dist/common/dto/get-media.dto.js.map +0 -1
- package/dist/database.d.ts +0 -4
- package/dist/database.js +0 -16
- package/dist/database.js.map +0 -1
- package/dist/dto/external-media.dto.d.ts +0 -10
- package/dist/dto/external-media.dto.js +0 -70
- package/dist/dto/external-media.dto.js.map +0 -1
- package/dist/dto/internal-medias.dto.d.ts +0 -12
- package/dist/dto/internal-medias.dto.js +0 -87
- package/dist/dto/internal-medias.dto.js.map +0 -1
- package/dist/entities/medias.entity.d.ts +0 -23
- package/dist/entities/medias.entity.js +0 -160
- package/dist/entities/medias.entity.js.map +0 -1
- package/dist/enum/index.d.ts +0 -2
- package/dist/enum/index.js +0 -6
- package/dist/enum/index.js.map +0 -1
- package/dist/enum/medias.enum.d.ts +0 -5
- package/dist/enum/medias.enum.js +0 -10
- package/dist/enum/medias.enum.js.map +0 -1
- package/dist/helpers/error.d.ts +0 -1
- package/dist/helpers/error.js +0 -13
- package/dist/helpers/error.js.map +0 -1
- package/dist/helpers/index.d.ts +0 -2
- package/dist/helpers/index.js +0 -6
- package/dist/helpers/index.js.map +0 -1
- package/dist/index.d.ts +0 -12
- package/dist/index.js +0 -22
- package/dist/index.js.map +0 -1
- package/dist/interfaces/base.medias-attr.interface.d.ts +0 -20
- package/dist/interfaces/base.medias-attr.interface.js +0 -3
- package/dist/interfaces/base.medias-attr.interface.js.map +0 -1
- package/dist/interfaces/medias.repository.interface.d.ts +0 -3
- package/dist/interfaces/medias.repository.interface.js +0 -3
- package/dist/interfaces/medias.repository.interface.js.map +0 -1
- package/dist/medias.d.ts +0 -65
- package/dist/medias.js +0 -564
- package/dist/medias.js.map +0 -1
- package/dist/medias.repository.d.ts +0 -8
- package/dist/medias.repository.js +0 -56
- package/dist/medias.repository.js.map +0 -1
- package/dist/pipe/append-id.pipe.d.ts +0 -6
- package/dist/pipe/append-id.pipe.js +0 -28
- package/dist/pipe/append-id.pipe.js.map +0 -1
- package/dist/pipe/validate-enum.pipe.d.ts +0 -6
- package/dist/pipe/validate-enum.pipe.js +0 -34
- package/dist/pipe/validate-enum.pipe.js.map +0 -1
- package/dist/pipe/validate-id.pipe.d.ts +0 -6
- package/dist/pipe/validate-id.pipe.js +0 -31
- package/dist/pipe/validate-id.pipe.js.map +0 -1
- package/dist/pipe/validate-search.pipe.d.ts +0 -7
- package/dist/pipe/validate-search.pipe.js +0 -46
- package/dist/pipe/validate-search.pipe.js.map +0 -1
- package/dist/responses/deleted.response.d.ts +0 -3
- package/dist/responses/deleted.response.js +0 -21
- package/dist/responses/deleted.response.js.map +0 -1
- package/dist/responses/pagination.response.d.ts +0 -5
- package/dist/responses/pagination.response.js +0 -26
- package/dist/responses/pagination.response.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
package/src/medias.ts
CHANGED
|
@@ -1,769 +1,769 @@
|
|
|
1
|
-
import { BlobServiceClient } from '@azure/storage-blob';
|
|
2
|
-
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
|
3
|
-
import * as crypto from 'crypto';
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import { CommonService } from './common/common.service';
|
|
6
|
-
import * as util from 'util';
|
|
7
|
-
import { BaseMedias } from './base/base.medias';
|
|
8
|
-
import { MediasModel } from './entities/medias.entity';
|
|
9
|
-
import { IBaseMediasAttr } from './interfaces/base.medias-attr.interface';
|
|
10
|
-
import { Readable } from 'stream';
|
|
11
|
-
import { InternalMediaDto } from './dto/internal-medias.dto';
|
|
12
|
-
import { ExternalMediaDto } from './dto/external-media.dto';
|
|
13
|
-
import * as cuid from 'cuid';
|
|
14
|
-
import { Op } from 'sequelize';
|
|
15
|
-
import { ActionEnum, Activity } from '@tomei/activity-history';
|
|
16
|
-
import { MediasRepository } from './medias.repository';
|
|
17
|
-
import { LoginUser } from '@tomei/sso';
|
|
18
|
-
|
|
19
|
-
type MediaFile = {
|
|
20
|
-
buffer: Buffer;
|
|
21
|
-
iv?: Buffer;
|
|
22
|
-
isEncrypted?: boolean;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type FilePath = {
|
|
26
|
-
filePath: string;
|
|
27
|
-
ivFilePath: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export class Medias extends BaseMedias {
|
|
31
|
-
ObjectName: string;
|
|
32
|
-
TableName: string;
|
|
33
|
-
ObjectId: string;
|
|
34
|
-
ObjectType = 'Media';
|
|
35
|
-
|
|
36
|
-
blobServiceClient = BlobServiceClient.fromConnectionString(
|
|
37
|
-
process.env.AZURE_STORAGE_CONNECTION_STRING,
|
|
38
|
-
);
|
|
39
|
-
container: string;
|
|
40
|
-
algorithm = 'aes-256-cbc';
|
|
41
|
-
commonService: CommonService;
|
|
42
|
-
entity = typeof MediasModel;
|
|
43
|
-
private static _Repo = new MediasRepository();
|
|
44
|
-
|
|
45
|
-
constructor(commonService?: CommonService, media?: IBaseMediasAttr) {
|
|
46
|
-
super(media);
|
|
47
|
-
this.commonService = commonService;
|
|
48
|
-
try {
|
|
49
|
-
this.container = process.env.MEDIA_AZUREBLOB_CONTAINER_NAME;
|
|
50
|
-
|
|
51
|
-
if (!this.container && this.IsStorageTypeAzure()) {
|
|
52
|
-
throw new BadRequestException(
|
|
53
|
-
'CONTAINER_NAME Environtment Variable name not found',
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public async createMedias(
|
|
62
|
-
isExternalMedias: boolean,
|
|
63
|
-
loginUser: LoginUser,
|
|
64
|
-
stream?: Express.Multer.File,
|
|
65
|
-
dbTransaction?: any,
|
|
66
|
-
) {
|
|
67
|
-
try {
|
|
68
|
-
const media = await super.create({
|
|
69
|
-
transaction: dbTransaction,
|
|
70
|
-
});
|
|
71
|
-
if (!isExternalMedias) {
|
|
72
|
-
await this.createInternalMedias(stream);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (media) {
|
|
76
|
-
const activity = new Activity();
|
|
77
|
-
activity.ActivityId = cuid();
|
|
78
|
-
activity.Action = ActionEnum.CREATE;
|
|
79
|
-
activity.Description = 'Add New Media';
|
|
80
|
-
activity.EntityType = 'Media';
|
|
81
|
-
activity.EntityId = this.MediaId;
|
|
82
|
-
activity.EntityValueBefore = JSON.stringify({});
|
|
83
|
-
activity.EntityValueAfter = JSON.stringify({
|
|
84
|
-
...media.get({ plain: true }),
|
|
85
|
-
});
|
|
86
|
-
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
87
|
-
}
|
|
88
|
-
return media;
|
|
89
|
-
} catch (error) {
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public async updateMedias(
|
|
95
|
-
isExternalMedias: boolean,
|
|
96
|
-
loginUser: LoginUser,
|
|
97
|
-
stream?: Express.Multer.File,
|
|
98
|
-
dbTransaction?: any,
|
|
99
|
-
) {
|
|
100
|
-
try {
|
|
101
|
-
const oldMedia = await super.findOne({
|
|
102
|
-
where: { MediaId: this.MediaId },
|
|
103
|
-
transaction: dbTransaction,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const oldMediaAttr = oldMedia.get({ plain: true });
|
|
107
|
-
|
|
108
|
-
const media = await super.update({
|
|
109
|
-
transaction: dbTransaction,
|
|
110
|
-
});
|
|
111
|
-
if (!isExternalMedias) {
|
|
112
|
-
await this.updateInternalMedia(stream, oldMediaAttr);
|
|
113
|
-
}
|
|
114
|
-
if (media) {
|
|
115
|
-
const activity = new Activity();
|
|
116
|
-
activity.ActivityId = cuid();
|
|
117
|
-
activity.Action = ActionEnum.UPDATE;
|
|
118
|
-
activity.Description = 'Update Media';
|
|
119
|
-
activity.EntityType = 'Media';
|
|
120
|
-
activity.EntityId = this.MediaId;
|
|
121
|
-
activity.EntityValueBefore = JSON.stringify({
|
|
122
|
-
...oldMedia.get({ plain: true }),
|
|
123
|
-
});
|
|
124
|
-
activity.EntityValueAfter = JSON.stringify({
|
|
125
|
-
...media.get({ plain: true }),
|
|
126
|
-
});
|
|
127
|
-
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return media;
|
|
131
|
-
} catch (error) {
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private IsStorageTypeAzure(): boolean {
|
|
137
|
-
const mediaStorageType = process.env.MEDIA_STORAGE_TYPE;
|
|
138
|
-
if (mediaStorageType === 'azure') {
|
|
139
|
-
return true;
|
|
140
|
-
} else {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private async updateInternalMedia(
|
|
146
|
-
stream: Express.Multer.File,
|
|
147
|
-
oldMedia: MediasModel,
|
|
148
|
-
): Promise<void> {
|
|
149
|
-
try {
|
|
150
|
-
this.FileExtension = this.FileExtension.split('.')[0];
|
|
151
|
-
this.FileName = this.FileName.replace(/\.[^/.]+$/, '');
|
|
152
|
-
|
|
153
|
-
let file: MediaFile;
|
|
154
|
-
if (this.IsEncryptedYN === 'Y') {
|
|
155
|
-
file = await this.encrypt(stream.buffer);
|
|
156
|
-
} else {
|
|
157
|
-
file = {
|
|
158
|
-
buffer: stream.buffer,
|
|
159
|
-
isEncrypted: false,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
if (this.IsStorageTypeAzure()) {
|
|
163
|
-
await this.deleteUploadedFileFromAzure(
|
|
164
|
-
oldMedia.URL,
|
|
165
|
-
oldMedia.FileName,
|
|
166
|
-
oldMedia.FileExtension,
|
|
167
|
-
oldMedia.IsEncryptedYN,
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
await this.uploadFileToAzure(file);
|
|
171
|
-
} else {
|
|
172
|
-
const filePath = super.createSaveLocation();
|
|
173
|
-
const isDirExist = util.promisify(fs.exists);
|
|
174
|
-
const isDir = await isDirExist(filePath);
|
|
175
|
-
if (isDir) {
|
|
176
|
-
fs.unlinkSync(oldMedia.FilePath);
|
|
177
|
-
if (this.IsEncryptedYN === 'Y') {
|
|
178
|
-
fs.unlinkSync(this.getIvPath());
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
await this.uploadFileToLocal(file);
|
|
182
|
-
}
|
|
183
|
-
} catch (error) {
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private async createInternalMedias(
|
|
189
|
-
stream: Express.Multer.File,
|
|
190
|
-
): Promise<void> {
|
|
191
|
-
try {
|
|
192
|
-
this.FileExtension = this.FileExtension.split('.')[0];
|
|
193
|
-
this.FileName = this.FileName.replace(/\.[^/.]+$/, '');
|
|
194
|
-
let file: MediaFile;
|
|
195
|
-
if (this.IsEncryptedYN === 'Y') {
|
|
196
|
-
file = await this.encrypt(stream.buffer);
|
|
197
|
-
} else {
|
|
198
|
-
file = {
|
|
199
|
-
buffer: stream.buffer,
|
|
200
|
-
isEncrypted: false,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
if (this.IsStorageTypeAzure()) {
|
|
204
|
-
this.uploadFileToAzure(file);
|
|
205
|
-
} else {
|
|
206
|
-
this.uploadFileToLocal(file);
|
|
207
|
-
}
|
|
208
|
-
} catch (error) {
|
|
209
|
-
throw error;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private getIvPath(oldUrl?: string) {
|
|
214
|
-
const url = oldUrl ? oldUrl : super.createSaveLocation();
|
|
215
|
-
return url + `/${this.FileName}iv.iv`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private async uploadFileToLocal(file: MediaFile): Promise<FilePath> {
|
|
219
|
-
try {
|
|
220
|
-
const savelocation = super.createSaveLocation();
|
|
221
|
-
const isDirExist = util.promisify(fs.exists);
|
|
222
|
-
const createDir = util.promisify(fs.mkdir);
|
|
223
|
-
const isDir = await isDirExist(savelocation);
|
|
224
|
-
if (!isDir) {
|
|
225
|
-
await createDir(savelocation, { recursive: true });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Make sure file is buffer not an ArrayBuffer
|
|
229
|
-
if (file.buffer instanceof ArrayBuffer) {
|
|
230
|
-
file.buffer = Buffer.from(file.buffer);
|
|
231
|
-
}
|
|
232
|
-
const writeFileContent = util.promisify(fs.writeFile);
|
|
233
|
-
await writeFileContent(this.FilePath, file.buffer);
|
|
234
|
-
if (file.isEncrypted) {
|
|
235
|
-
await writeFileContent(this.getIvPath(), file.iv);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const path: FilePath = {
|
|
239
|
-
filePath: super.createSaveLocation(),
|
|
240
|
-
ivFilePath: this.getIvPath() ? this.getIvPath() : '',
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
return path;
|
|
244
|
-
} catch (error) {
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private async deleteUploadedFileFromLocal(
|
|
250
|
-
path: string,
|
|
251
|
-
isEncryptedYN: string,
|
|
252
|
-
): Promise<void> {
|
|
253
|
-
try {
|
|
254
|
-
if (!fs.existsSync(path)) return;
|
|
255
|
-
|
|
256
|
-
const stats = fs.statSync(path);
|
|
257
|
-
if (stats.isDirectory()) {
|
|
258
|
-
fs.rmSync(path, { recursive: true, force: true });
|
|
259
|
-
} else {
|
|
260
|
-
fs.unlinkSync(path);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (isEncryptedYN === 'Y' && fs.existsSync(this.getIvPath())) {
|
|
264
|
-
fs.unlinkSync(this.getIvPath());
|
|
265
|
-
}
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.error('Error deleting file/directory:', error);
|
|
268
|
-
throw error;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private async uploadFileToAzure(file: MediaFile): Promise<FilePath> {
|
|
273
|
-
const filePath = await this.uploadFileToAzureStorage(
|
|
274
|
-
file.buffer,
|
|
275
|
-
this.ObjectType,
|
|
276
|
-
this.ObjectId,
|
|
277
|
-
this.FileName,
|
|
278
|
-
this.FileExtension,
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
let ivPath: string;
|
|
282
|
-
if (this.IsEncryptedYN === 'Y') {
|
|
283
|
-
ivPath = await this.uploadFileToAzureStorage(
|
|
284
|
-
file.iv,
|
|
285
|
-
this.ObjectType,
|
|
286
|
-
this.ObjectId,
|
|
287
|
-
this.FileName + 'iv',
|
|
288
|
-
'iv',
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const path: FilePath = {
|
|
293
|
-
filePath,
|
|
294
|
-
ivFilePath: ivPath,
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
return path;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private async deleteUploadedFileFromAzure(
|
|
301
|
-
path: string,
|
|
302
|
-
FileName: string,
|
|
303
|
-
FileExtension: string,
|
|
304
|
-
isEncryptedYN: string,
|
|
305
|
-
): Promise<void> {
|
|
306
|
-
try {
|
|
307
|
-
await this.deleteUploadedFileToAzureStorage(
|
|
308
|
-
path,
|
|
309
|
-
FileName,
|
|
310
|
-
FileExtension,
|
|
311
|
-
);
|
|
312
|
-
if (isEncryptedYN === 'Y') {
|
|
313
|
-
await this.deleteUploadedFileToAzureStorage(
|
|
314
|
-
path,
|
|
315
|
-
FileName + 'iv',
|
|
316
|
-
'.iv',
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
} catch (error) {
|
|
320
|
-
throw error;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
private async uploadFileToAzureStorage(
|
|
325
|
-
bufferFile: Buffer,
|
|
326
|
-
ObjectType: string,
|
|
327
|
-
ObjectId: string,
|
|
328
|
-
FileName: string,
|
|
329
|
-
FileExtension: string,
|
|
330
|
-
): Promise<string> {
|
|
331
|
-
try {
|
|
332
|
-
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
333
|
-
const path = `${this.container}/${ObjectType}/${ObjectId}`;
|
|
334
|
-
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
335
|
-
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
336
|
-
`${fileName}.${FileExtension}`,
|
|
337
|
-
);
|
|
338
|
-
await blockBlobClient.uploadData(bufferFile);
|
|
339
|
-
return path;
|
|
340
|
-
} catch (error) {
|
|
341
|
-
throw error;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
private async deleteUploadedFileToAzureStorage(
|
|
346
|
-
path: string,
|
|
347
|
-
FileName: string,
|
|
348
|
-
FileExtension: string,
|
|
349
|
-
): Promise<any> {
|
|
350
|
-
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
351
|
-
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
352
|
-
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
353
|
-
`${fileName}.${FileExtension}`,
|
|
354
|
-
);
|
|
355
|
-
return await blockBlobClient.deleteIfExists();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
private setKey(): string {
|
|
359
|
-
try {
|
|
360
|
-
const key = process.env.MEDIA_ENCRYPT_KEY;
|
|
361
|
-
if (!key) {
|
|
362
|
-
throw new BadRequestException(
|
|
363
|
-
'MEDIA_ENCRYPT_KEY Environtment Variable name not found',
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
return key;
|
|
367
|
-
} catch (error) {
|
|
368
|
-
throw error;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async findFile(): Promise<Buffer> {
|
|
373
|
-
try {
|
|
374
|
-
let fileBuffer: Buffer;
|
|
375
|
-
if (this.IsStorageTypeAzure()) {
|
|
376
|
-
fileBuffer = await this.getFileFromAzure(
|
|
377
|
-
this.URL,
|
|
378
|
-
this.FileName,
|
|
379
|
-
this.FileExtension,
|
|
380
|
-
);
|
|
381
|
-
} else {
|
|
382
|
-
try {
|
|
383
|
-
fileBuffer = await this.getFileFromLocal(this.FilePath);
|
|
384
|
-
} catch (error) {
|
|
385
|
-
if (error.includes('ENOENT')) {
|
|
386
|
-
console.warn(
|
|
387
|
-
`Local file missing: ${this.FilePath}, fetching from Azure.`,
|
|
388
|
-
);
|
|
389
|
-
fileBuffer = await this.getFileFromAzure(
|
|
390
|
-
this.URL,
|
|
391
|
-
this.FileName,
|
|
392
|
-
this.FileExtension,
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
const file: MediaFile = {
|
|
396
|
-
buffer: fileBuffer,
|
|
397
|
-
isEncrypted: false,
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
this.uploadFileToLocal(file);
|
|
401
|
-
} else {
|
|
402
|
-
throw error;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
let file: MediaFile = {
|
|
408
|
-
buffer: fileBuffer,
|
|
409
|
-
isEncrypted: this.IsEncryptedYN === 'Y' ? true : false,
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
if (file.isEncrypted) {
|
|
413
|
-
try {
|
|
414
|
-
if (this.IsStorageTypeAzure()) {
|
|
415
|
-
file.iv = await this.getFileFromAzure(
|
|
416
|
-
this.URL,
|
|
417
|
-
this.FileName + 'iv',
|
|
418
|
-
'iv',
|
|
419
|
-
);
|
|
420
|
-
} else {
|
|
421
|
-
file.iv = await this.getFileFromLocal(this.getIvPath());
|
|
422
|
-
}
|
|
423
|
-
} catch (error) {
|
|
424
|
-
if (error.includes('ENOENT')) {
|
|
425
|
-
console.warn(
|
|
426
|
-
`IV file missing: ${this.getIvPath()}, fetching from Azure.`,
|
|
427
|
-
);
|
|
428
|
-
file.iv = await this.getFileFromAzure(
|
|
429
|
-
this.URL,
|
|
430
|
-
this.FileName + 'iv',
|
|
431
|
-
'iv',
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
this.uploadFileToLocal(file);
|
|
435
|
-
} else {
|
|
436
|
-
throw error;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
file = await this.decrypt(file);
|
|
441
|
-
}
|
|
442
|
-
return file.buffer;
|
|
443
|
-
} catch (error) {
|
|
444
|
-
throw error;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
private async stream2buffer(
|
|
449
|
-
stream: Readable | fs.ReadStream,
|
|
450
|
-
): Promise<Buffer> {
|
|
451
|
-
return new Promise<Buffer>((resolve, reject) => {
|
|
452
|
-
const buffer = Array<any>();
|
|
453
|
-
|
|
454
|
-
stream.on('data', (chunk) => buffer.push(chunk));
|
|
455
|
-
stream.on('end', () => resolve(Buffer.concat(buffer)));
|
|
456
|
-
stream.on('error', (err) => reject(`error converting stream - ${err}`));
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
private async getFileFromAzure(
|
|
461
|
-
path: string,
|
|
462
|
-
FileName: string,
|
|
463
|
-
FileExtension: string,
|
|
464
|
-
): Promise<Buffer> {
|
|
465
|
-
try {
|
|
466
|
-
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
467
|
-
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
468
|
-
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
469
|
-
`${fileName}.${FileExtension}`,
|
|
470
|
-
);
|
|
471
|
-
const downloadResponse = await blockBlobClient.download();
|
|
472
|
-
const ReadStream = new Readable().wrap(
|
|
473
|
-
downloadResponse.readableStreamBody,
|
|
474
|
-
);
|
|
475
|
-
const file = await this.stream2buffer(ReadStream);
|
|
476
|
-
return file;
|
|
477
|
-
} catch (error) {
|
|
478
|
-
throw error;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
private async getFileFromLocal(pathfile: string): Promise<Buffer> {
|
|
483
|
-
try {
|
|
484
|
-
const stream = fs.createReadStream(pathfile);
|
|
485
|
-
const file = await this.stream2buffer(stream);
|
|
486
|
-
return file;
|
|
487
|
-
} catch (error) {
|
|
488
|
-
throw error;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
public async encrypt(fileBuffer: Buffer): Promise<MediaFile> {
|
|
493
|
-
try {
|
|
494
|
-
const key = this.setKey();
|
|
495
|
-
const iv = crypto.randomBytes(16);
|
|
496
|
-
|
|
497
|
-
const cipher = crypto.createCipheriv(this.algorithm, key, iv);
|
|
498
|
-
const resultBuffer = Buffer.concat([
|
|
499
|
-
cipher.update(fileBuffer),
|
|
500
|
-
cipher.final(),
|
|
501
|
-
]);
|
|
502
|
-
|
|
503
|
-
const result: MediaFile = {
|
|
504
|
-
buffer: resultBuffer,
|
|
505
|
-
iv,
|
|
506
|
-
isEncrypted: true,
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
return result;
|
|
510
|
-
} catch (error) {
|
|
511
|
-
throw error;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
public async decrypt(file: MediaFile): Promise<MediaFile> {
|
|
516
|
-
try {
|
|
517
|
-
const key = this.setKey();
|
|
518
|
-
const iv = file.iv;
|
|
519
|
-
const decipher = crypto.createDecipheriv(this.algorithm, key, iv);
|
|
520
|
-
const resultBuffer = Buffer.concat([
|
|
521
|
-
decipher.update(file.buffer),
|
|
522
|
-
decipher.final(),
|
|
523
|
-
]);
|
|
524
|
-
|
|
525
|
-
const result: MediaFile = {
|
|
526
|
-
buffer: resultBuffer,
|
|
527
|
-
isEncrypted: false,
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
return result;
|
|
531
|
-
} catch (error) {
|
|
532
|
-
throw error;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
public async delete(loginUser: LoginUser, dbTransaction?: any): Promise<any> {
|
|
537
|
-
try {
|
|
538
|
-
const data: MediasModel = await super.delete({
|
|
539
|
-
transaction: dbTransaction,
|
|
540
|
-
});
|
|
541
|
-
if (this.IsExternalYN === 'N') {
|
|
542
|
-
if (this.IsStorageTypeAzure()) {
|
|
543
|
-
await this.deleteUploadedFileFromAzure(
|
|
544
|
-
this.URL,
|
|
545
|
-
this.FileName,
|
|
546
|
-
this.FileExtension,
|
|
547
|
-
this.IsEncryptedYN,
|
|
548
|
-
);
|
|
549
|
-
} else {
|
|
550
|
-
await this.deleteUploadedFileFromLocal(this.URL, this.IsEncryptedYN);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
const activity = new Activity();
|
|
554
|
-
activity.ActivityId = cuid();
|
|
555
|
-
activity.Action = ActionEnum.DELETE;
|
|
556
|
-
activity.Description = 'Deleted media (ID: ${this.MediaId})';
|
|
557
|
-
activity.EntityType = 'Media';
|
|
558
|
-
activity.EntityId = this.MediaId;
|
|
559
|
-
activity.EntityValueBefore = JSON.stringify(data);
|
|
560
|
-
activity.EntityValueAfter = JSON.stringify({});
|
|
561
|
-
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
562
|
-
return { message: 'Media has been deleted.' };
|
|
563
|
-
} catch (error) {
|
|
564
|
-
throw error;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
async postInternal(
|
|
569
|
-
fileStream: Express.Multer.File,
|
|
570
|
-
createMediaDto: InternalMediaDto,
|
|
571
|
-
loginUser: LoginUser,
|
|
572
|
-
dbTransaction?: any,
|
|
573
|
-
) {
|
|
574
|
-
try {
|
|
575
|
-
const mediaAttr: IBaseMediasAttr = {
|
|
576
|
-
...createMediaDto,
|
|
577
|
-
MediaId: cuid(),
|
|
578
|
-
IsExternalYN: 'N',
|
|
579
|
-
ExternalSource: '',
|
|
580
|
-
URL: '',
|
|
581
|
-
FilePath: '',
|
|
582
|
-
CreatedAt: new Date(),
|
|
583
|
-
UpdatedAt: new Date(),
|
|
584
|
-
CreatedById: loginUser.ObjectId,
|
|
585
|
-
UpdatedById: loginUser.ObjectId,
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
this.init(mediaAttr);
|
|
589
|
-
const createdData = await this.createMedias(
|
|
590
|
-
false,
|
|
591
|
-
loginUser,
|
|
592
|
-
fileStream,
|
|
593
|
-
dbTransaction,
|
|
594
|
-
);
|
|
595
|
-
return createdData;
|
|
596
|
-
} catch (error) {
|
|
597
|
-
throw error;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
async postExternal(createMediaDto: ExternalMediaDto, loginUser: LoginUser) {
|
|
602
|
-
const mediaAttr: IBaseMediasAttr = {
|
|
603
|
-
...createMediaDto,
|
|
604
|
-
MediaId: cuid(),
|
|
605
|
-
IsExternalYN: 'Y',
|
|
606
|
-
CreatedAt: new Date(),
|
|
607
|
-
UpdatedAt: new Date(),
|
|
608
|
-
IsEncryptedYN: 'N',
|
|
609
|
-
FilePath: '',
|
|
610
|
-
FileName: '',
|
|
611
|
-
FileExtension: '',
|
|
612
|
-
CreatedById: loginUser.ObjectId,
|
|
613
|
-
UpdatedById: loginUser.ObjectId,
|
|
614
|
-
};
|
|
615
|
-
this.init(mediaAttr);
|
|
616
|
-
await this.createMedias(true, loginUser);
|
|
617
|
-
return mediaAttr;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
async getAll(rows: number, page: number, search: string) {
|
|
621
|
-
let searchObj: any;
|
|
622
|
-
try {
|
|
623
|
-
searchObj = search ? JSON.parse(search) : {};
|
|
624
|
-
} catch (err) {
|
|
625
|
-
throw new BadRequestException('Bad value for search.');
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const { ...mediaFilter } = searchObj;
|
|
629
|
-
const queryObj = {};
|
|
630
|
-
|
|
631
|
-
Object.entries(mediaFilter).forEach(([key, value]) => {
|
|
632
|
-
queryObj[key] = {
|
|
633
|
-
[Op.substring]: value,
|
|
634
|
-
};
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
const offset = rows * (page - 1);
|
|
638
|
-
|
|
639
|
-
const options = {
|
|
640
|
-
order: [['CreatedAt', 'DESC']],
|
|
641
|
-
limit: rows,
|
|
642
|
-
offset,
|
|
643
|
-
where: queryObj,
|
|
644
|
-
distinct: true,
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
return this.findAllWithPagination(options);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
async getOne(id: string, dbTransaction?: any) {
|
|
651
|
-
try {
|
|
652
|
-
const media = await this.findOne({
|
|
653
|
-
where: { MediaId: id },
|
|
654
|
-
transaction: dbTransaction,
|
|
655
|
-
});
|
|
656
|
-
if (!media) {
|
|
657
|
-
throw new NotFoundException(`Media not found with id ${id}`);
|
|
658
|
-
}
|
|
659
|
-
return media;
|
|
660
|
-
} catch (error) {
|
|
661
|
-
throw error;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
async getFile(id: string, dbTransaction?: any) {
|
|
666
|
-
try {
|
|
667
|
-
const media = await this.findOne({
|
|
668
|
-
where: { MediaId: id },
|
|
669
|
-
transaction: dbTransaction,
|
|
670
|
-
});
|
|
671
|
-
if (!media) {
|
|
672
|
-
throw new NotFoundException(`Media not found with id ${id}`);
|
|
673
|
-
}
|
|
674
|
-
this.init({ ...media.get({ plain: true }) });
|
|
675
|
-
const fileBuffer: Buffer = await this.findFile();
|
|
676
|
-
return {
|
|
677
|
-
fileBuffer,
|
|
678
|
-
resOption: {
|
|
679
|
-
'Content-Type': 'application/octet-stream',
|
|
680
|
-
'Content-Disposition': `attachment; filename=${media.FileName}.${media.FileExtension}`,
|
|
681
|
-
'Content-Length': fileBuffer.length,
|
|
682
|
-
},
|
|
683
|
-
};
|
|
684
|
-
} catch (error) {
|
|
685
|
-
throw error;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
async remove(id: string, loginUser: LoginUser, dbTransaction?: any) {
|
|
690
|
-
try {
|
|
691
|
-
const media = await this.findOne({
|
|
692
|
-
where: { MediaId: id },
|
|
693
|
-
transaction: dbTransaction,
|
|
694
|
-
});
|
|
695
|
-
if (!media) {
|
|
696
|
-
throw new NotFoundException(`Media not found with id ${id}`);
|
|
697
|
-
}
|
|
698
|
-
const mediaAttr: IBaseMediasAttr = {
|
|
699
|
-
...media.get({ plain: true }),
|
|
700
|
-
UpdatedAt: new Date(),
|
|
701
|
-
UpdatedById: loginUser.ObjectId,
|
|
702
|
-
};
|
|
703
|
-
this.init(mediaAttr);
|
|
704
|
-
return await this.delete(loginUser, dbTransaction);
|
|
705
|
-
} catch (error) {
|
|
706
|
-
throw error;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
async putExternal(
|
|
711
|
-
id: string,
|
|
712
|
-
updatedMediaDto: ExternalMediaDto,
|
|
713
|
-
userId: string,
|
|
714
|
-
dbTransaction?: any,
|
|
715
|
-
) {
|
|
716
|
-
try {
|
|
717
|
-
const media = await this.findOne({
|
|
718
|
-
where: { MediaId: id },
|
|
719
|
-
transaction: dbTransaction,
|
|
720
|
-
});
|
|
721
|
-
if (!media) {
|
|
722
|
-
throw new NotFoundException(`Media not found with id ${id}`);
|
|
723
|
-
}
|
|
724
|
-
const mediaAttr: IBaseMediasAttr = {
|
|
725
|
-
...media.get({ plain: true }),
|
|
726
|
-
...updatedMediaDto,
|
|
727
|
-
UpdatedAt: new Date(),
|
|
728
|
-
UpdatedById: userId,
|
|
729
|
-
};
|
|
730
|
-
this.init(mediaAttr);
|
|
731
|
-
return await this.updateMedias(true, null, dbTransaction);
|
|
732
|
-
} catch (error) {
|
|
733
|
-
throw error;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
async putInternal(
|
|
738
|
-
fileStream: Express.Multer.File,
|
|
739
|
-
id: string,
|
|
740
|
-
updatedMediaDto: InternalMediaDto,
|
|
741
|
-
loginUser: LoginUser,
|
|
742
|
-
dbTransaction?: any,
|
|
743
|
-
) {
|
|
744
|
-
try {
|
|
745
|
-
const media = await this.findOne({
|
|
746
|
-
where: { MediaId: id },
|
|
747
|
-
transaction: dbTransaction,
|
|
748
|
-
});
|
|
749
|
-
if (!media) {
|
|
750
|
-
throw new NotFoundException(`Media not found with id ${id}`);
|
|
751
|
-
}
|
|
752
|
-
const mediaAttr: IBaseMediasAttr = {
|
|
753
|
-
...media.get({ plain: true }),
|
|
754
|
-
...updatedMediaDto,
|
|
755
|
-
UpdatedAt: new Date(),
|
|
756
|
-
UpdatedById: loginUser.ObjectId,
|
|
757
|
-
};
|
|
758
|
-
this.init(mediaAttr);
|
|
759
|
-
return await this.updateMedias(
|
|
760
|
-
false,
|
|
761
|
-
loginUser,
|
|
762
|
-
fileStream,
|
|
763
|
-
dbTransaction,
|
|
764
|
-
);
|
|
765
|
-
} catch (error) {
|
|
766
|
-
throw error;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
}
|
|
1
|
+
import { BlobServiceClient } from '@azure/storage-blob';
|
|
2
|
+
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import { CommonService } from './common/common.service';
|
|
6
|
+
import * as util from 'util';
|
|
7
|
+
import { BaseMedias } from './base/base.medias';
|
|
8
|
+
import { MediasModel } from './entities/medias.entity';
|
|
9
|
+
import { IBaseMediasAttr } from './interfaces/base.medias-attr.interface';
|
|
10
|
+
import { Readable } from 'stream';
|
|
11
|
+
import { InternalMediaDto } from './dto/internal-medias.dto';
|
|
12
|
+
import { ExternalMediaDto } from './dto/external-media.dto';
|
|
13
|
+
import * as cuid from 'cuid';
|
|
14
|
+
import { Op } from 'sequelize';
|
|
15
|
+
import { ActionEnum, Activity } from '@tomei/activity-history';
|
|
16
|
+
import { MediasRepository } from './medias.repository';
|
|
17
|
+
import { LoginUser } from '@tomei/sso';
|
|
18
|
+
|
|
19
|
+
type MediaFile = {
|
|
20
|
+
buffer: Buffer;
|
|
21
|
+
iv?: Buffer;
|
|
22
|
+
isEncrypted?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type FilePath = {
|
|
26
|
+
filePath: string;
|
|
27
|
+
ivFilePath: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class Medias extends BaseMedias {
|
|
31
|
+
ObjectName: string;
|
|
32
|
+
TableName: string;
|
|
33
|
+
ObjectId: string;
|
|
34
|
+
ObjectType = 'Media';
|
|
35
|
+
|
|
36
|
+
blobServiceClient = BlobServiceClient.fromConnectionString(
|
|
37
|
+
process.env.AZURE_STORAGE_CONNECTION_STRING,
|
|
38
|
+
);
|
|
39
|
+
container: string;
|
|
40
|
+
algorithm = 'aes-256-cbc';
|
|
41
|
+
commonService: CommonService;
|
|
42
|
+
entity = typeof MediasModel;
|
|
43
|
+
private static _Repo = new MediasRepository();
|
|
44
|
+
|
|
45
|
+
constructor(commonService?: CommonService, media?: IBaseMediasAttr) {
|
|
46
|
+
super(media);
|
|
47
|
+
this.commonService = commonService;
|
|
48
|
+
try {
|
|
49
|
+
this.container = process.env.MEDIA_AZUREBLOB_CONTAINER_NAME;
|
|
50
|
+
|
|
51
|
+
if (!this.container && this.IsStorageTypeAzure()) {
|
|
52
|
+
throw new BadRequestException(
|
|
53
|
+
'CONTAINER_NAME Environtment Variable name not found',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async createMedias(
|
|
62
|
+
isExternalMedias: boolean,
|
|
63
|
+
loginUser: LoginUser,
|
|
64
|
+
stream?: Express.Multer.File,
|
|
65
|
+
dbTransaction?: any,
|
|
66
|
+
) {
|
|
67
|
+
try {
|
|
68
|
+
const media = await super.create({
|
|
69
|
+
transaction: dbTransaction,
|
|
70
|
+
});
|
|
71
|
+
if (!isExternalMedias) {
|
|
72
|
+
await this.createInternalMedias(stream);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (media) {
|
|
76
|
+
const activity = new Activity();
|
|
77
|
+
activity.ActivityId = cuid();
|
|
78
|
+
activity.Action = ActionEnum.CREATE;
|
|
79
|
+
activity.Description = 'Add New Media';
|
|
80
|
+
activity.EntityType = 'Media';
|
|
81
|
+
activity.EntityId = this.MediaId;
|
|
82
|
+
activity.EntityValueBefore = JSON.stringify({});
|
|
83
|
+
activity.EntityValueAfter = JSON.stringify({
|
|
84
|
+
...media.get({ plain: true }),
|
|
85
|
+
});
|
|
86
|
+
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
87
|
+
}
|
|
88
|
+
return media;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public async updateMedias(
|
|
95
|
+
isExternalMedias: boolean,
|
|
96
|
+
loginUser: LoginUser,
|
|
97
|
+
stream?: Express.Multer.File,
|
|
98
|
+
dbTransaction?: any,
|
|
99
|
+
) {
|
|
100
|
+
try {
|
|
101
|
+
const oldMedia = await super.findOne({
|
|
102
|
+
where: { MediaId: this.MediaId },
|
|
103
|
+
transaction: dbTransaction,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const oldMediaAttr = oldMedia.get({ plain: true });
|
|
107
|
+
|
|
108
|
+
const media = await super.update({
|
|
109
|
+
transaction: dbTransaction,
|
|
110
|
+
});
|
|
111
|
+
if (!isExternalMedias) {
|
|
112
|
+
await this.updateInternalMedia(stream, oldMediaAttr);
|
|
113
|
+
}
|
|
114
|
+
if (media) {
|
|
115
|
+
const activity = new Activity();
|
|
116
|
+
activity.ActivityId = cuid();
|
|
117
|
+
activity.Action = ActionEnum.UPDATE;
|
|
118
|
+
activity.Description = 'Update Media';
|
|
119
|
+
activity.EntityType = 'Media';
|
|
120
|
+
activity.EntityId = this.MediaId;
|
|
121
|
+
activity.EntityValueBefore = JSON.stringify({
|
|
122
|
+
...oldMedia.get({ plain: true }),
|
|
123
|
+
});
|
|
124
|
+
activity.EntityValueAfter = JSON.stringify({
|
|
125
|
+
...media.get({ plain: true }),
|
|
126
|
+
});
|
|
127
|
+
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return media;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private IsStorageTypeAzure(): boolean {
|
|
137
|
+
const mediaStorageType = process.env.MEDIA_STORAGE_TYPE;
|
|
138
|
+
if (mediaStorageType === 'azure') {
|
|
139
|
+
return true;
|
|
140
|
+
} else {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async updateInternalMedia(
|
|
146
|
+
stream: Express.Multer.File,
|
|
147
|
+
oldMedia: MediasModel,
|
|
148
|
+
): Promise<void> {
|
|
149
|
+
try {
|
|
150
|
+
this.FileExtension = this.FileExtension.split('.')[0];
|
|
151
|
+
this.FileName = this.FileName.replace(/\.[^/.]+$/, '');
|
|
152
|
+
|
|
153
|
+
let file: MediaFile;
|
|
154
|
+
if (this.IsEncryptedYN === 'Y') {
|
|
155
|
+
file = await this.encrypt(stream.buffer);
|
|
156
|
+
} else {
|
|
157
|
+
file = {
|
|
158
|
+
buffer: stream.buffer,
|
|
159
|
+
isEncrypted: false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (this.IsStorageTypeAzure()) {
|
|
163
|
+
await this.deleteUploadedFileFromAzure(
|
|
164
|
+
oldMedia.URL,
|
|
165
|
+
oldMedia.FileName,
|
|
166
|
+
oldMedia.FileExtension,
|
|
167
|
+
oldMedia.IsEncryptedYN,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
await this.uploadFileToAzure(file);
|
|
171
|
+
} else {
|
|
172
|
+
const filePath = super.createSaveLocation();
|
|
173
|
+
const isDirExist = util.promisify(fs.exists);
|
|
174
|
+
const isDir = await isDirExist(filePath);
|
|
175
|
+
if (isDir) {
|
|
176
|
+
fs.unlinkSync(oldMedia.FilePath);
|
|
177
|
+
if (this.IsEncryptedYN === 'Y') {
|
|
178
|
+
fs.unlinkSync(this.getIvPath());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
await this.uploadFileToLocal(file);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private async createInternalMedias(
|
|
189
|
+
stream: Express.Multer.File,
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
try {
|
|
192
|
+
this.FileExtension = this.FileExtension.split('.')[0];
|
|
193
|
+
this.FileName = this.FileName.replace(/\.[^/.]+$/, '');
|
|
194
|
+
let file: MediaFile;
|
|
195
|
+
if (this.IsEncryptedYN === 'Y') {
|
|
196
|
+
file = await this.encrypt(stream.buffer);
|
|
197
|
+
} else {
|
|
198
|
+
file = {
|
|
199
|
+
buffer: stream.buffer,
|
|
200
|
+
isEncrypted: false,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (this.IsStorageTypeAzure()) {
|
|
204
|
+
this.uploadFileToAzure(file);
|
|
205
|
+
} else {
|
|
206
|
+
this.uploadFileToLocal(file);
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private getIvPath(oldUrl?: string) {
|
|
214
|
+
const url = oldUrl ? oldUrl : super.createSaveLocation();
|
|
215
|
+
return url + `/${this.FileName}iv.iv`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private async uploadFileToLocal(file: MediaFile): Promise<FilePath> {
|
|
219
|
+
try {
|
|
220
|
+
const savelocation = super.createSaveLocation();
|
|
221
|
+
const isDirExist = util.promisify(fs.exists);
|
|
222
|
+
const createDir = util.promisify(fs.mkdir);
|
|
223
|
+
const isDir = await isDirExist(savelocation);
|
|
224
|
+
if (!isDir) {
|
|
225
|
+
await createDir(savelocation, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Make sure file is buffer not an ArrayBuffer
|
|
229
|
+
if (file.buffer instanceof ArrayBuffer) {
|
|
230
|
+
file.buffer = Buffer.from(file.buffer);
|
|
231
|
+
}
|
|
232
|
+
const writeFileContent = util.promisify(fs.writeFile);
|
|
233
|
+
await writeFileContent(this.FilePath, file.buffer);
|
|
234
|
+
if (file.isEncrypted) {
|
|
235
|
+
await writeFileContent(this.getIvPath(), file.iv);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const path: FilePath = {
|
|
239
|
+
filePath: super.createSaveLocation(),
|
|
240
|
+
ivFilePath: this.getIvPath() ? this.getIvPath() : '',
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return path;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private async deleteUploadedFileFromLocal(
|
|
250
|
+
path: string,
|
|
251
|
+
isEncryptedYN: string,
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
try {
|
|
254
|
+
if (!fs.existsSync(path)) return;
|
|
255
|
+
|
|
256
|
+
const stats = fs.statSync(path);
|
|
257
|
+
if (stats.isDirectory()) {
|
|
258
|
+
fs.rmSync(path, { recursive: true, force: true });
|
|
259
|
+
} else {
|
|
260
|
+
fs.unlinkSync(path);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (isEncryptedYN === 'Y' && fs.existsSync(this.getIvPath())) {
|
|
264
|
+
fs.unlinkSync(this.getIvPath());
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('Error deleting file/directory:', error);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private async uploadFileToAzure(file: MediaFile): Promise<FilePath> {
|
|
273
|
+
const filePath = await this.uploadFileToAzureStorage(
|
|
274
|
+
file.buffer,
|
|
275
|
+
this.ObjectType,
|
|
276
|
+
this.ObjectId,
|
|
277
|
+
this.FileName,
|
|
278
|
+
this.FileExtension,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
let ivPath: string;
|
|
282
|
+
if (this.IsEncryptedYN === 'Y') {
|
|
283
|
+
ivPath = await this.uploadFileToAzureStorage(
|
|
284
|
+
file.iv,
|
|
285
|
+
this.ObjectType,
|
|
286
|
+
this.ObjectId,
|
|
287
|
+
this.FileName + 'iv',
|
|
288
|
+
'iv',
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const path: FilePath = {
|
|
293
|
+
filePath,
|
|
294
|
+
ivFilePath: ivPath,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return path;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private async deleteUploadedFileFromAzure(
|
|
301
|
+
path: string,
|
|
302
|
+
FileName: string,
|
|
303
|
+
FileExtension: string,
|
|
304
|
+
isEncryptedYN: string,
|
|
305
|
+
): Promise<void> {
|
|
306
|
+
try {
|
|
307
|
+
await this.deleteUploadedFileToAzureStorage(
|
|
308
|
+
path,
|
|
309
|
+
FileName,
|
|
310
|
+
FileExtension,
|
|
311
|
+
);
|
|
312
|
+
if (isEncryptedYN === 'Y') {
|
|
313
|
+
await this.deleteUploadedFileToAzureStorage(
|
|
314
|
+
path,
|
|
315
|
+
FileName + 'iv',
|
|
316
|
+
'.iv',
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private async uploadFileToAzureStorage(
|
|
325
|
+
bufferFile: Buffer,
|
|
326
|
+
ObjectType: string,
|
|
327
|
+
ObjectId: string,
|
|
328
|
+
FileName: string,
|
|
329
|
+
FileExtension: string,
|
|
330
|
+
): Promise<string> {
|
|
331
|
+
try {
|
|
332
|
+
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
333
|
+
const path = `${this.container}/${ObjectType}/${ObjectId}`;
|
|
334
|
+
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
335
|
+
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
336
|
+
`${fileName}.${FileExtension}`,
|
|
337
|
+
);
|
|
338
|
+
await blockBlobClient.uploadData(bufferFile);
|
|
339
|
+
return path;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async deleteUploadedFileToAzureStorage(
|
|
346
|
+
path: string,
|
|
347
|
+
FileName: string,
|
|
348
|
+
FileExtension: string,
|
|
349
|
+
): Promise<any> {
|
|
350
|
+
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
351
|
+
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
352
|
+
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
353
|
+
`${fileName}.${FileExtension}`,
|
|
354
|
+
);
|
|
355
|
+
return await blockBlobClient.deleteIfExists();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private setKey(): string {
|
|
359
|
+
try {
|
|
360
|
+
const key = process.env.MEDIA_ENCRYPT_KEY;
|
|
361
|
+
if (!key) {
|
|
362
|
+
throw new BadRequestException(
|
|
363
|
+
'MEDIA_ENCRYPT_KEY Environtment Variable name not found',
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return key;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async findFile(): Promise<Buffer> {
|
|
373
|
+
try {
|
|
374
|
+
let fileBuffer: Buffer;
|
|
375
|
+
if (this.IsStorageTypeAzure()) {
|
|
376
|
+
fileBuffer = await this.getFileFromAzure(
|
|
377
|
+
this.URL,
|
|
378
|
+
this.FileName,
|
|
379
|
+
this.FileExtension,
|
|
380
|
+
);
|
|
381
|
+
} else {
|
|
382
|
+
try {
|
|
383
|
+
fileBuffer = await this.getFileFromLocal(this.FilePath);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (error.includes('ENOENT')) {
|
|
386
|
+
console.warn(
|
|
387
|
+
`Local file missing: ${this.FilePath}, fetching from Azure.`,
|
|
388
|
+
);
|
|
389
|
+
fileBuffer = await this.getFileFromAzure(
|
|
390
|
+
this.URL,
|
|
391
|
+
this.FileName,
|
|
392
|
+
this.FileExtension,
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const file: MediaFile = {
|
|
396
|
+
buffer: fileBuffer,
|
|
397
|
+
isEncrypted: false,
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
await this.uploadFileToLocal(file);
|
|
401
|
+
} else {
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
let file: MediaFile = {
|
|
408
|
+
buffer: fileBuffer,
|
|
409
|
+
isEncrypted: this.IsEncryptedYN === 'Y' ? true : false,
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
if (file.isEncrypted) {
|
|
413
|
+
try {
|
|
414
|
+
if (this.IsStorageTypeAzure()) {
|
|
415
|
+
file.iv = await this.getFileFromAzure(
|
|
416
|
+
this.URL,
|
|
417
|
+
this.FileName + 'iv',
|
|
418
|
+
'iv',
|
|
419
|
+
);
|
|
420
|
+
} else {
|
|
421
|
+
file.iv = await this.getFileFromLocal(this.getIvPath());
|
|
422
|
+
}
|
|
423
|
+
} catch (error) {
|
|
424
|
+
if (error.includes('ENOENT')) {
|
|
425
|
+
console.warn(
|
|
426
|
+
`IV file missing: ${this.getIvPath()}, fetching from Azure.`,
|
|
427
|
+
);
|
|
428
|
+
file.iv = await this.getFileFromAzure(
|
|
429
|
+
this.URL,
|
|
430
|
+
this.FileName + 'iv',
|
|
431
|
+
'iv',
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
await this.uploadFileToLocal(file);
|
|
435
|
+
} else {
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
file = await this.decrypt(file);
|
|
441
|
+
}
|
|
442
|
+
return file.buffer;
|
|
443
|
+
} catch (error) {
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private async stream2buffer(
|
|
449
|
+
stream: Readable | fs.ReadStream,
|
|
450
|
+
): Promise<Buffer> {
|
|
451
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
452
|
+
const buffer = Array<any>();
|
|
453
|
+
|
|
454
|
+
stream.on('data', (chunk) => buffer.push(chunk));
|
|
455
|
+
stream.on('end', () => resolve(Buffer.concat(buffer)));
|
|
456
|
+
stream.on('error', (err) => reject(`error converting stream - ${err}`));
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private async getFileFromAzure(
|
|
461
|
+
path: string,
|
|
462
|
+
FileName: string,
|
|
463
|
+
FileExtension: string,
|
|
464
|
+
): Promise<Buffer> {
|
|
465
|
+
try {
|
|
466
|
+
const fileName = FileName.replace(/\.[^/.]+$/, '');
|
|
467
|
+
const blobcontainer = this.blobServiceClient.getContainerClient(path);
|
|
468
|
+
const blockBlobClient = await blobcontainer.getBlockBlobClient(
|
|
469
|
+
`${fileName}.${FileExtension}`,
|
|
470
|
+
);
|
|
471
|
+
const downloadResponse = await blockBlobClient.download();
|
|
472
|
+
const ReadStream = new Readable().wrap(
|
|
473
|
+
downloadResponse.readableStreamBody,
|
|
474
|
+
);
|
|
475
|
+
const file = await this.stream2buffer(ReadStream);
|
|
476
|
+
return file;
|
|
477
|
+
} catch (error) {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private async getFileFromLocal(pathfile: string): Promise<Buffer> {
|
|
483
|
+
try {
|
|
484
|
+
const stream = fs.createReadStream(pathfile);
|
|
485
|
+
const file = await this.stream2buffer(stream);
|
|
486
|
+
return file;
|
|
487
|
+
} catch (error) {
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
public async encrypt(fileBuffer: Buffer): Promise<MediaFile> {
|
|
493
|
+
try {
|
|
494
|
+
const key = this.setKey();
|
|
495
|
+
const iv = crypto.randomBytes(16);
|
|
496
|
+
|
|
497
|
+
const cipher = crypto.createCipheriv(this.algorithm, key, iv);
|
|
498
|
+
const resultBuffer = Buffer.concat([
|
|
499
|
+
cipher.update(fileBuffer),
|
|
500
|
+
cipher.final(),
|
|
501
|
+
]);
|
|
502
|
+
|
|
503
|
+
const result: MediaFile = {
|
|
504
|
+
buffer: resultBuffer,
|
|
505
|
+
iv,
|
|
506
|
+
isEncrypted: true,
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
return result;
|
|
510
|
+
} catch (error) {
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
public async decrypt(file: MediaFile): Promise<MediaFile> {
|
|
516
|
+
try {
|
|
517
|
+
const key = this.setKey();
|
|
518
|
+
const iv = file.iv;
|
|
519
|
+
const decipher = crypto.createDecipheriv(this.algorithm, key, iv);
|
|
520
|
+
const resultBuffer = Buffer.concat([
|
|
521
|
+
decipher.update(file.buffer),
|
|
522
|
+
decipher.final(),
|
|
523
|
+
]);
|
|
524
|
+
|
|
525
|
+
const result: MediaFile = {
|
|
526
|
+
buffer: resultBuffer,
|
|
527
|
+
isEncrypted: false,
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
return result;
|
|
531
|
+
} catch (error) {
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
public async delete(loginUser: LoginUser, dbTransaction?: any): Promise<any> {
|
|
537
|
+
try {
|
|
538
|
+
const data: MediasModel = await super.delete({
|
|
539
|
+
transaction: dbTransaction,
|
|
540
|
+
});
|
|
541
|
+
if (this.IsExternalYN === 'N') {
|
|
542
|
+
if (this.IsStorageTypeAzure()) {
|
|
543
|
+
await this.deleteUploadedFileFromAzure(
|
|
544
|
+
this.URL,
|
|
545
|
+
this.FileName,
|
|
546
|
+
this.FileExtension,
|
|
547
|
+
this.IsEncryptedYN,
|
|
548
|
+
);
|
|
549
|
+
} else {
|
|
550
|
+
await this.deleteUploadedFileFromLocal(this.URL, this.IsEncryptedYN);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const activity = new Activity();
|
|
554
|
+
activity.ActivityId = cuid();
|
|
555
|
+
activity.Action = ActionEnum.DELETE;
|
|
556
|
+
activity.Description = 'Deleted media (ID: ${this.MediaId})';
|
|
557
|
+
activity.EntityType = 'Media';
|
|
558
|
+
activity.EntityId = this.MediaId;
|
|
559
|
+
activity.EntityValueBefore = JSON.stringify(data);
|
|
560
|
+
activity.EntityValueAfter = JSON.stringify({});
|
|
561
|
+
await activity.create(loginUser.ObjectId, dbTransaction);
|
|
562
|
+
return { message: 'Media has been deleted.' };
|
|
563
|
+
} catch (error) {
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async postInternal(
|
|
569
|
+
fileStream: Express.Multer.File,
|
|
570
|
+
createMediaDto: InternalMediaDto,
|
|
571
|
+
loginUser: LoginUser,
|
|
572
|
+
dbTransaction?: any,
|
|
573
|
+
) {
|
|
574
|
+
try {
|
|
575
|
+
const mediaAttr: IBaseMediasAttr = {
|
|
576
|
+
...createMediaDto,
|
|
577
|
+
MediaId: cuid(),
|
|
578
|
+
IsExternalYN: 'N',
|
|
579
|
+
ExternalSource: '',
|
|
580
|
+
URL: '',
|
|
581
|
+
FilePath: '',
|
|
582
|
+
CreatedAt: new Date(),
|
|
583
|
+
UpdatedAt: new Date(),
|
|
584
|
+
CreatedById: loginUser.ObjectId,
|
|
585
|
+
UpdatedById: loginUser.ObjectId,
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
this.init(mediaAttr);
|
|
589
|
+
const createdData = await this.createMedias(
|
|
590
|
+
false,
|
|
591
|
+
loginUser,
|
|
592
|
+
fileStream,
|
|
593
|
+
dbTransaction,
|
|
594
|
+
);
|
|
595
|
+
return createdData;
|
|
596
|
+
} catch (error) {
|
|
597
|
+
throw error;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async postExternal(createMediaDto: ExternalMediaDto, loginUser: LoginUser) {
|
|
602
|
+
const mediaAttr: IBaseMediasAttr = {
|
|
603
|
+
...createMediaDto,
|
|
604
|
+
MediaId: cuid(),
|
|
605
|
+
IsExternalYN: 'Y',
|
|
606
|
+
CreatedAt: new Date(),
|
|
607
|
+
UpdatedAt: new Date(),
|
|
608
|
+
IsEncryptedYN: 'N',
|
|
609
|
+
FilePath: '',
|
|
610
|
+
FileName: '',
|
|
611
|
+
FileExtension: '',
|
|
612
|
+
CreatedById: loginUser.ObjectId,
|
|
613
|
+
UpdatedById: loginUser.ObjectId,
|
|
614
|
+
};
|
|
615
|
+
this.init(mediaAttr);
|
|
616
|
+
await this.createMedias(true, loginUser);
|
|
617
|
+
return mediaAttr;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async getAll(rows: number, page: number, search: string) {
|
|
621
|
+
let searchObj: any;
|
|
622
|
+
try {
|
|
623
|
+
searchObj = search ? JSON.parse(search) : {};
|
|
624
|
+
} catch (err) {
|
|
625
|
+
throw new BadRequestException('Bad value for search.');
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const { ...mediaFilter } = searchObj;
|
|
629
|
+
const queryObj = {};
|
|
630
|
+
|
|
631
|
+
Object.entries(mediaFilter).forEach(([key, value]) => {
|
|
632
|
+
queryObj[key] = {
|
|
633
|
+
[Op.substring]: value,
|
|
634
|
+
};
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const offset = rows * (page - 1);
|
|
638
|
+
|
|
639
|
+
const options = {
|
|
640
|
+
order: [['CreatedAt', 'DESC']],
|
|
641
|
+
limit: rows,
|
|
642
|
+
offset,
|
|
643
|
+
where: queryObj,
|
|
644
|
+
distinct: true,
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
return this.findAllWithPagination(options);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
async getOne(id: string, dbTransaction?: any) {
|
|
651
|
+
try {
|
|
652
|
+
const media = await this.findOne({
|
|
653
|
+
where: { MediaId: id },
|
|
654
|
+
transaction: dbTransaction,
|
|
655
|
+
});
|
|
656
|
+
if (!media) {
|
|
657
|
+
throw new NotFoundException(`Media not found with id ${id}`);
|
|
658
|
+
}
|
|
659
|
+
return media;
|
|
660
|
+
} catch (error) {
|
|
661
|
+
throw error;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
async getFile(id: string, dbTransaction?: any) {
|
|
666
|
+
try {
|
|
667
|
+
const media = await this.findOne({
|
|
668
|
+
where: { MediaId: id },
|
|
669
|
+
transaction: dbTransaction,
|
|
670
|
+
});
|
|
671
|
+
if (!media) {
|
|
672
|
+
throw new NotFoundException(`Media not found with id ${id}`);
|
|
673
|
+
}
|
|
674
|
+
this.init({ ...media.get({ plain: true }) });
|
|
675
|
+
const fileBuffer: Buffer = await this.findFile();
|
|
676
|
+
return {
|
|
677
|
+
fileBuffer,
|
|
678
|
+
resOption: {
|
|
679
|
+
'Content-Type': 'application/octet-stream',
|
|
680
|
+
'Content-Disposition': `attachment; filename=${media.FileName}.${media.FileExtension}`,
|
|
681
|
+
'Content-Length': fileBuffer.length,
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
} catch (error) {
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async remove(id: string, loginUser: LoginUser, dbTransaction?: any) {
|
|
690
|
+
try {
|
|
691
|
+
const media = await this.findOne({
|
|
692
|
+
where: { MediaId: id },
|
|
693
|
+
transaction: dbTransaction,
|
|
694
|
+
});
|
|
695
|
+
if (!media) {
|
|
696
|
+
throw new NotFoundException(`Media not found with id ${id}`);
|
|
697
|
+
}
|
|
698
|
+
const mediaAttr: IBaseMediasAttr = {
|
|
699
|
+
...media.get({ plain: true }),
|
|
700
|
+
UpdatedAt: new Date(),
|
|
701
|
+
UpdatedById: loginUser.ObjectId,
|
|
702
|
+
};
|
|
703
|
+
this.init(mediaAttr);
|
|
704
|
+
return await this.delete(loginUser, dbTransaction);
|
|
705
|
+
} catch (error) {
|
|
706
|
+
throw error;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async putExternal(
|
|
711
|
+
id: string,
|
|
712
|
+
updatedMediaDto: ExternalMediaDto,
|
|
713
|
+
userId: string,
|
|
714
|
+
dbTransaction?: any,
|
|
715
|
+
) {
|
|
716
|
+
try {
|
|
717
|
+
const media = await this.findOne({
|
|
718
|
+
where: { MediaId: id },
|
|
719
|
+
transaction: dbTransaction,
|
|
720
|
+
});
|
|
721
|
+
if (!media) {
|
|
722
|
+
throw new NotFoundException(`Media not found with id ${id}`);
|
|
723
|
+
}
|
|
724
|
+
const mediaAttr: IBaseMediasAttr = {
|
|
725
|
+
...media.get({ plain: true }),
|
|
726
|
+
...updatedMediaDto,
|
|
727
|
+
UpdatedAt: new Date(),
|
|
728
|
+
UpdatedById: userId,
|
|
729
|
+
};
|
|
730
|
+
this.init(mediaAttr);
|
|
731
|
+
return await this.updateMedias(true, null, dbTransaction);
|
|
732
|
+
} catch (error) {
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
async putInternal(
|
|
738
|
+
fileStream: Express.Multer.File,
|
|
739
|
+
id: string,
|
|
740
|
+
updatedMediaDto: InternalMediaDto,
|
|
741
|
+
loginUser: LoginUser,
|
|
742
|
+
dbTransaction?: any,
|
|
743
|
+
) {
|
|
744
|
+
try {
|
|
745
|
+
const media = await this.findOne({
|
|
746
|
+
where: { MediaId: id },
|
|
747
|
+
transaction: dbTransaction,
|
|
748
|
+
});
|
|
749
|
+
if (!media) {
|
|
750
|
+
throw new NotFoundException(`Media not found with id ${id}`);
|
|
751
|
+
}
|
|
752
|
+
const mediaAttr: IBaseMediasAttr = {
|
|
753
|
+
...media.get({ plain: true }),
|
|
754
|
+
...updatedMediaDto,
|
|
755
|
+
UpdatedAt: new Date(),
|
|
756
|
+
UpdatedById: loginUser.ObjectId,
|
|
757
|
+
};
|
|
758
|
+
this.init(mediaAttr);
|
|
759
|
+
return await this.updateMedias(
|
|
760
|
+
false,
|
|
761
|
+
loginUser,
|
|
762
|
+
fileStream,
|
|
763
|
+
dbTransaction,
|
|
764
|
+
);
|
|
765
|
+
} catch (error) {
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|