-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathuploads.service.js
More file actions
114 lines (100 loc) · 3.75 KB
/
uploads.service.js
File metadata and controls
114 lines (100 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* Module dependencies
*/
import crypto from 'crypto';
import config from '../../../config/index.js';
import AppError from '../../../lib/helpers/AppError.js';
import multerService from '../../../lib/services/multer.js';
import gridfs from '../../../lib/services/gridfs.js';
import UploadRepository from '../repositories/uploads.repository.js';
const MIME_TO_EXT = {
'image/jpeg': 'jpeg',
'image/jpg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'application/pdf': 'pdf',
// Allows downstream features that store rendered HTML snapshots in GridFS
// alongside binary uploads (e.g. error pages, scrap previews).
'text/html': 'html',
};
/**
* @desc Function to ask repository to get an upload
* @param {String} uploadName
* @return {Promise} Upload
*/
const get = async (uploadName) => {
const result = await UploadRepository.get(uploadName);
return Promise.resolve(result);
};
/**
* @desc Function to ask repository to get stream of chunks data
* @param {Object} Upload
* @return {Promise} result stream
*/
const getStream = async (upload) => {
const result = await UploadRepository.getStream(upload);
return Promise.resolve(result);
};
/**
* @desc Function to ask repository to update an upload
* @param {Object} req.file
* @param {Object} User
* @param {String} kind, upload configuration path (important for futur transformations)
* @return {Promise} Upload
*/
const update = async (file, user, kind) => {
const update = {
filename: await multerService.generateFileName(file.filename || file.originalname),
metadata: {
user: user.id,
kind: kind || null,
},
};
const result = await UploadRepository.update(file._id, update);
return Promise.resolve(result);
};
/**
* @desc Function to ask repository to remove chunks data
* @param {Object} Upload
* @return {Promise} confirmation of delete
*/
const remove = async (upload) => {
const result = await UploadRepository.remove(upload);
return Promise.resolve(result);
};
/**
* @desc Store a buffer as an upload programmatically (no HTTP request)
* @param {Buffer} buffer - File content
* @param {string} contentType - MIME type (e.g., 'image/jpeg')
* @param {string} kind - Upload kind matching config (e.g., 'snapshot')
* @param {Object} metadata - Additional metadata (user, organizationId, etc.)
* @returns {Promise<Object>} The created upload document
*/
const createFromBuffer = async (buffer, contentType, kind, metadata = {}) => {
if (!Buffer.isBuffer(buffer)) {
throw new AppError('Upload: buffer is required and must be a Buffer', { code: 'SERVICE_ERROR', status: 422 });
}
const kindConfig = config.uploads?.[kind];
if (!kindConfig) throw new AppError(`Upload: unknown kind "${kind}"`, { code: 'SERVICE_ERROR', status: 422 });
if (!Array.isArray(kindConfig.formats)) {
throw new AppError(`Upload: kind "${kind}" has no formats configured`, { code: 'SERVICE_ERROR', status: 500 });
}
if (!kindConfig.formats.includes(contentType)) {
throw new AppError(`Upload: content type "${contentType}" not allowed for kind "${kind}"`, { code: 'SERVICE_ERROR', status: 422 });
}
if (kindConfig.limits?.fileSize && buffer.length > kindConfig.limits.fileSize) {
throw new AppError(`Upload: buffer size ${buffer.length} exceeds limit ${kindConfig.limits.fileSize}`, { code: 'SERVICE_ERROR', status: 422 });
}
const rawExt = config.uploads?.mimeTypes?.[contentType] || MIME_TO_EXT[contentType];
const ext = rawExt && /^[a-zA-Z0-9]+$/.test(rawExt) ? rawExt : 'bin';
const filename = `${crypto.randomBytes(32).toString('hex')}.${ext}`;
const result = await gridfs.createFromBuffer(buffer, filename, contentType, { ...metadata, kind, contentType });
return result;
};
export default {
get,
getStream,
update,
remove,
createFromBuffer,
};