node-server-engine 1.2.4 → 1.2.6

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 (56) hide show
  1. package/.env.example +1 -4
  2. package/README.md +214 -18
  3. package/dist/entities/Server/Server.js +0 -10
  4. package/dist/entities/Server/Server.js.map +1 -1
  5. package/dist/middleware/fileUploader/fileUploader.d.ts +4 -5
  6. package/dist/middleware/fileUploader/fileUploader.js +68 -255
  7. package/dist/middleware/fileUploader/fileUploader.js.map +1 -1
  8. package/dist/middleware/fileUploader/fileUploader.types.d.ts +13 -32
  9. package/dist/middleware/fileUploader/fileUploader.types.js.map +1 -1
  10. package/dist/middleware/geminiFileUploader/geminiFileUploader.js +21 -69
  11. package/dist/middleware/geminiFileUploader/geminiFileUploader.js.map +1 -1
  12. package/dist/middleware/index.d.ts +1 -0
  13. package/dist/middleware/index.js +11 -0
  14. package/dist/middleware/index.js.map +1 -1
  15. package/dist/middleware/multiPartFileUploader/index.d.ts +1 -0
  16. package/dist/middleware/multiPartFileUploader/index.js +17 -0
  17. package/dist/middleware/multiPartFileUploader/index.js.map +1 -0
  18. package/dist/middleware/multiPartFileUploader/multiPartFileUploader.d.ts +3 -0
  19. package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js +156 -0
  20. package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js.map +1 -0
  21. package/dist/utils/createHttpServer/createHttpServer.js.map +1 -1
  22. package/dist/utils/geminiFileUpload/geminiFileUpload.d.ts +9 -0
  23. package/dist/utils/geminiFileUpload/geminiFileUpload.js +100 -0
  24. package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -0
  25. package/dist/utils/geminiFileUpload/index.d.ts +1 -0
  26. package/dist/utils/geminiFileUpload/index.js +17 -0
  27. package/dist/utils/geminiFileUpload/index.js.map +1 -0
  28. package/dist/utils/index.d.ts +1 -0
  29. package/dist/utils/index.js +11 -0
  30. package/dist/utils/index.js.map +1 -1
  31. package/package.json +3 -1
  32. package/src/entities/Server/Server.ts +0 -6
  33. package/src/middleware/fileUploader/fileUploader.ts +70 -270
  34. package/src/middleware/fileUploader/fileUploader.types.ts +16 -33
  35. package/src/middleware/geminiFileUploader/geminiFileUploader.ts +19 -68
  36. package/src/middleware/index.ts +1 -0
  37. package/src/middleware/multiPartFileUploader/index.ts +1 -0
  38. package/src/middleware/multiPartFileUploader/multiPartFileUploader.ts +184 -0
  39. package/src/utils/createHttpServer/createHttpServer.ts +2 -3
  40. package/src/utils/geminiFileUpload/geminiFileUpload.ts +98 -0
  41. package/src/utils/geminiFileUpload/index.ts +1 -0
  42. package/src/utils/index.ts +3 -0
  43. package/test/env.ts +1 -0
  44. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.d.ts +0 -6
  45. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.js +0 -28
  46. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.js.map +0 -1
  47. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.d.ts +0 -12
  48. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.js +0 -2
  49. package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.js.map +0 -1
  50. package/dist/middleware/fileUploader/compressAvatar/index.d.ts +0 -2
  51. package/dist/middleware/fileUploader/compressAvatar/index.js +0 -28
  52. package/dist/middleware/fileUploader/compressAvatar/index.js.map +0 -1
  53. package/src/middleware/fileUploader/compressAvatar/compressAvatar.ts +0 -25
  54. package/src/middleware/fileUploader/compressAvatar/compressAvatar.types.ts +0 -12
  55. package/src/middleware/fileUploader/compressAvatar/index.ts +0 -2
  56. package/src/middleware/fileUploader/fileUploader.test.ts +0 -310
@@ -5,292 +5,105 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.fileUploader = fileUploader;
8
- exports.fileUploaderMiddleware = fileUploaderMiddleware;
9
- var _busboy = _interopRequireDefault(require("busboy"));
10
- var _compressAvatar = require("./compressAvatar");
11
- var _isMultiPart = require("./isMultiPart");
12
- var _validate = require("./validate");
13
- var _Storage = require("../../entities/Storage");
14
8
  var _WebError = require("../../entities/WebError");
15
9
  var _report = require("../../utils/report");
10
+ var _multer = _interopRequireDefault(require("multer"));
11
+ var _validate = require("./validate");
16
12
  const namespace = 'engine:middleware:fileUploader';
17
13
 
18
14
  /**
19
- * Generate a custom fileUploader middleware
15
+ * Middleware for handling file uploads.
16
+ * @param filesOptions Configuration options for expected files.
17
+ * @param validator Validation schema for request fields.
20
18
  */
21
- function fileUploader(options, validator) {
22
- return (request, response, next) => fileUploaderMiddleware(request, response, next, options, validator);
23
- }
24
-
25
- /** Handle file upload and generic multi-part parsing */
26
- function fileUploaderMiddleware(request, response, next, filesOptions, validator) {
27
- (0, _report.reportDebug)({
28
- namespace,
29
- message: 'Received request to upload file',
30
- data: {
31
- filesOptions,
32
- validator
33
- }
34
- });
35
- // Check that the request is a proper multipart request
36
- if (!(0, _isMultiPart.isMultipart)(request)) throw new _WebError.WebError({
37
- statusCode: 415,
38
- errorCode: 'invalid-content-type',
39
- message: 'Request is not of type multipart/form-data',
40
- data: {
41
- contentType: request.get('Content-Type')
42
- }
43
- });
44
- // If no files to handle ignore
45
- if (!filesOptions.length) next();
46
- // Parse multipart data with Busboy
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- const busboy = new _busboy.default({
49
- headers: {
50
- ...request.headers,
51
- 'content-type': request.headers['content-type'] ?? ''
52
- }
53
- });
54
- let hasError = false;
55
-
56
- /**
57
- * Function called when an error happened in the process
58
- * It immediately stops and drains all connection and deletes all files that have already been uploaded
59
- */
60
- function triggerError(error) {
61
- pendingWrites--;
62
- if (hasError) return;
63
- hasError = true;
64
- request.unpipe(busboy);
65
- request.on('readable', request.read.bind(request));
66
- busboy.removeAllListeners();
67
- next(error);
68
- if (request.files) {
69
- Object.values(request.files).forEach(files => {
70
- files.forEach(file => {
71
- _Storage.Storage.delete(file.bucket, file.name).catch(error => {
72
- (0, _report.reportError)(error);
73
- });
74
- });
75
- });
76
- }
77
- }
78
- let readFinished = false;
79
- let fieldsValidated = false;
80
- let pendingWrites = 0;
81
-
82
- /** Function called when all the body has been parsed */
83
- function done() {
84
- if (readFinished && pendingWrites === 0 && fieldsValidated && !hasError) {
85
- const uploadedFiles = Object.keys(request.files ?? {});
86
- if (filesOptions.some(options => options.required && !uploadedFiles.includes(options.key))) {
87
- triggerError(new _WebError.WebError({
88
- statusCode: 400,
89
- errorCode: 'missing-field',
90
- message: 'Some required files have not been uploaded',
91
- data: {
92
- filesOptions,
93
- validator,
94
- uploadedFiles
95
- }
96
- }));
97
- return;
98
- }
99
- next();
100
- }
101
- }
102
-
103
- /*
104
- * On cancel request, while processing
105
- */
106
- request.connection.on('close', function () {
107
- if (!response.writableEnded) triggerError(new Error('Request cancelled'));
108
- });
109
-
110
- /**
111
- * Once all the fields have been read they are checked against the validation schema
112
- */
113
- async function validateFields() {
114
- try {
115
- await (0, _validate.validate)(request, validator);
116
- } catch (error) {
117
- triggerError(error);
118
- }
19
+ function fileUploader(filesOptions, validator) {
20
+ return async (request, response, next) => {
119
21
  (0, _report.reportDebug)({
120
22
  namespace,
121
- message: 'Validation processed',
23
+ message: 'Received request to upload file',
122
24
  data: {
123
- hasError,
124
25
  filesOptions,
125
26
  validator
126
27
  }
127
28
  });
128
- fieldsValidated = true;
129
- done();
130
- }
131
- busboy.on('file', (field, file, fileName, encoding, mimeType) => {
132
- (async () => {
133
- (0, _report.reportDebug)({
134
- namespace,
135
- message: 'Found file in request',
136
- data: {
137
- hasError,
138
- field,
139
- fileName,
140
- encoding,
141
- mimeType,
142
- pendingWrites
143
- }
144
- });
145
- // Do not handle the stream if there has been an error previously
146
- if (hasError) file.destroy();
147
- pendingWrites++;
148
- // Find related file options based on the field name
149
- const options = filesOptions.find(fileOption => fileOption.key === field);
150
- // Make sure that the field is in the config
151
- if (!options) {
152
- triggerError(new _WebError.WebError({
153
- statusCode: 400,
154
- errorCode: 'invalid-file-field',
155
- message: 'Request contains a file at an invalid field',
156
- hint: {
157
- field
158
- },
29
+
30
+ // Setup multer with memory storage
31
+ const storage = _multer.default.memoryStorage();
32
+ const upload = (0, _multer.default)({
33
+ storage,
34
+ limits: {
35
+ fileSize: 10 * 1024 * 1024
36
+ } // Limit file size to 10MB
37
+ }).any();
38
+ upload(request, response, async err => {
39
+ if (err) {
40
+ (0, _report.reportDebug)({
41
+ namespace,
42
+ message: 'Multer upload error',
159
43
  data: {
160
- field
44
+ error: err.message
161
45
  }
162
- }));
163
- return;
46
+ });
47
+ return next(err);
164
48
  }
165
- (0, _report.reportDebug)({
166
- namespace,
167
- message: 'Found options for file',
168
- data: {
169
- hasError,
170
- field,
171
- fileName,
172
- encoding,
173
- mimeType,
174
- pendingWrites,
175
- options
176
- }
177
- });
178
- // If a specific MIME type is required check it
179
- if (options.mimeTypes && !options.mimeTypes.includes(mimeType)) {
180
- triggerError(new _WebError.WebError({
181
- statusCode: 415,
182
- errorCode: 'invalid-mime-type',
183
- message: 'Request contains a file with an invalid MIME type',
184
- hint: {
185
- field
186
- },
187
- data: {
188
- field,
189
- mimeType,
190
- expected: options.mimeTypes
191
- }
192
- }));
193
- return;
49
+
50
+ // Ensure `request.files` is properly typed
51
+ let uploadedFiles = [];
52
+ if (Array.isArray(request.files)) {
53
+ uploadedFiles = request.files;
54
+ } else if (typeof request.files === 'object') {
55
+ uploadedFiles = Object.values(request.files).flat();
194
56
  }
195
- // Generate the destination path of the file and upload
57
+
58
+ // Log uploaded files
196
59
  (0, _report.reportDebug)({
197
60
  namespace,
198
- message: 'Starting file upload',
61
+ message: 'Files Received',
199
62
  data: {
200
- hasError,
201
- field,
202
- fileName,
203
- encoding,
204
- mimeType,
205
- pendingWrites,
206
- options
63
+ uploadedFiles: uploadedFiles.map(file => file.originalname)
207
64
  }
208
65
  });
66
+
67
+ // Validate request body fields
209
68
  try {
210
- const fileToUpload = options.image ? (0, _compressAvatar.compressAvatar)(file, options.image) : file;
211
- const uploadedFile = await _Storage.Storage.upload(fileToUpload, options.bucket, {
212
- directory: options.path,
213
- mime: mimeType,
214
- noExtension: options.noExtension
215
- }, {
216
- metadata: {
217
- contentType: mimeType
69
+ await (0, _validate.validate)(request, validator);
70
+ } catch (error) {
71
+ (0, _report.reportDebug)({
72
+ namespace,
73
+ message: 'Validation failed',
74
+ data: {
75
+ error
218
76
  }
219
- }, {
220
- maxSize: options.maxSize
221
77
  });
78
+ return next(error);
79
+ }
80
+
81
+ // Extract uploaded field names
82
+ const uploadedFileKeys = uploadedFiles.map(file => file.fieldname);
83
+
84
+ // Check for missing required files
85
+ const missingFiles = filesOptions.filter(option => option.required && !uploadedFileKeys.includes(option.key));
86
+ if (missingFiles.length > 0) {
222
87
  (0, _report.reportDebug)({
223
88
  namespace,
224
- message: 'File uploaded',
89
+ message: 'Missing required files',
225
90
  data: {
226
- hasError,
227
- field,
228
- fileName,
229
- encoding,
230
- mimeType,
231
- pendingWrites,
232
- options,
233
- uploadedFile
91
+ missingFiles,
92
+ uploadedFileKeys
234
93
  }
235
94
  });
236
- if (hasError) {
237
- // If some other failed before this one terminated
238
- // Delete file
239
- await _Storage.Storage.delete(uploadedFile.bucket, uploadedFile.name);
240
- } else {
241
- // Handle successful upload
242
- // Add the file's data to the request
243
- pendingWrites--;
244
- if (!request.files) request.files = {};
245
- if (!request.files[field]) request.files[field] = [];
246
- request.files[field].push({
247
- ...uploadedFile,
248
- originalFileName: fileName
249
- });
250
- done();
251
- }
252
- } catch (error) {
253
- triggerError(error);
95
+ return next(new _WebError.WebError({
96
+ statusCode: 400,
97
+ errorCode: 'missing-field',
98
+ message: 'Some required files have not been uploaded',
99
+ data: {
100
+ missingFiles,
101
+ uploadedFileKeys
102
+ }
103
+ }));
254
104
  }
255
- })().catch(error => {
256
- triggerError(error);
257
- });
258
- });
259
-
260
- /**
261
- * Handle non file fields
262
- * These fields will be pushed to the body as array of strings
263
- */
264
- busboy.on('field', (fieldName, value) => {
265
- if (!request.body) request.body = {};
266
- if (request.body[fieldName]) {
267
- request.body[fieldName].push(value);
268
- } else {
269
- request.body[fieldName] = [value];
270
- }
271
- });
272
- busboy.on('error', function (error) {
273
- triggerError(error);
274
- });
275
- busboy.on('finish', () => {
276
- (async () => {
277
- (0, _report.reportDebug)({
278
- namespace,
279
- message: 'Busboy finished reading',
280
- data: {
281
- hasError,
282
- filesOptions,
283
- validator,
284
- pendingWrites
285
- }
286
- });
287
- readFinished = true;
288
- await validateFields();
289
- done();
290
- })().catch(error => {
291
- triggerError(error);
105
+ next();
292
106
  });
293
- });
294
- return request.pipe(busboy);
107
+ };
295
108
  }
296
109
  //# sourceMappingURL=fileUploader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fileUploader.js","names":["_busboy","_interopRequireDefault","require","_compressAvatar","_isMultiPart","_validate","_Storage","_WebError","_report","namespace","fileUploader","options","validator","request","response","next","fileUploaderMiddleware","filesOptions","reportDebug","message","data","isMultipart","WebError","statusCode","errorCode","contentType","get","length","busboy","Busboy","headers","hasError","triggerError","error","pendingWrites","unpipe","on","read","bind","removeAllListeners","files","Object","values","forEach","file","Storage","delete","bucket","name","catch","reportError","readFinished","fieldsValidated","done","uploadedFiles","keys","some","required","includes","key","connection","writableEnded","Error","validateFields","validate","field","fileName","encoding","mimeType","destroy","find","fileOption","hint","mimeTypes","expected","fileToUpload","image","compressAvatar","uploadedFile","upload","directory","path","mime","noExtension","metadata","maxSize","push","originalFileName","fieldName","value","body","pipe"],"sources":["../../../src/middleware/fileUploader/fileUploader.ts"],"sourcesContent":["import Busboy from 'busboy';\nimport { Request, Response, NextFunction } from 'express';\nimport { Schema } from 'express-validator';\nimport { compressAvatar } from './compressAvatar';\nimport { FileUploaderConfig } from './fileUploader.types';\nimport { isMultipart } from './isMultiPart';\nimport { validate } from './validate';\nimport { Storage } from 'entities/Storage';\nimport { WebError } from 'entities/WebError';\nimport { reportError, reportDebug } from 'utils/report';\nimport { Readable } from 'stream';\n\nconst namespace = 'engine:middleware:fileUploader';\n\n/**\n * Generate a custom fileUploader middleware\n */\nexport function fileUploader(\n options: Array<FileUploaderConfig>,\n validator: Schema\n) {\n return (\n request: Request,\n response: Response,\n next: NextFunction\n ): Busboy.Busboy =>\n fileUploaderMiddleware(request, response, next, options, validator);\n}\n\n/** Handle file upload and generic multi-part parsing */\nexport function fileUploaderMiddleware(\n request: Request,\n response: Response,\n next: NextFunction,\n filesOptions: Array<FileUploaderConfig>,\n validator: Schema\n): Busboy.Busboy {\n reportDebug({\n namespace,\n message: 'Received request to upload file',\n data: { filesOptions, validator }\n });\n // Check that the request is a proper multipart request\n if (!isMultipart(request))\n throw new WebError({\n statusCode: 415,\n errorCode: 'invalid-content-type',\n message: 'Request is not of type multipart/form-data',\n data: { contentType: request.get('Content-Type') }\n });\n // If no files to handle ignore\n if (!filesOptions.length) next();\n // Parse multipart data with Busboy\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const busboy = new (Busboy as any)({\n headers: {\n ...request.headers,\n 'content-type': request.headers['content-type'] ?? ''\n }\n });\n\n let hasError = false;\n\n /**\n * Function called when an error happened in the process\n * It immediately stops and drains all connection and deletes all files that have already been uploaded\n */\n function triggerError(error: unknown): void {\n pendingWrites--;\n if (hasError) return;\n hasError = true;\n request.unpipe(busboy);\n request.on('readable', request.read.bind(request));\n busboy.removeAllListeners();\n next(error);\n if (request.files) {\n Object.values(request.files).forEach((files) => {\n files.forEach((file) => {\n Storage.delete(file.bucket, file.name).catch((error) => {\n reportError(error);\n });\n });\n });\n }\n }\n\n let readFinished = false;\n let fieldsValidated = false;\n let pendingWrites = 0;\n\n /** Function called when all the body has been parsed */\n function done(): void {\n if (readFinished && pendingWrites === 0 && fieldsValidated && !hasError) {\n const uploadedFiles = Object.keys(request.files ?? {});\n if (\n filesOptions.some(\n (options) => options.required && !uploadedFiles.includes(options.key)\n )\n ) {\n triggerError(\n new WebError({\n statusCode: 400,\n errorCode: 'missing-field',\n message: 'Some required files have not been uploaded',\n data: { filesOptions, validator, uploadedFiles }\n })\n );\n return;\n }\n next();\n }\n }\n\n /*\n * On cancel request, while processing\n */\n request.connection.on('close', function () {\n if (!response.writableEnded) triggerError(new Error('Request cancelled'));\n });\n\n /**\n * Once all the fields have been read they are checked against the validation schema\n */\n async function validateFields(): Promise<void> {\n try {\n await validate(request, validator);\n } catch (error) {\n triggerError(error);\n }\n reportDebug({\n namespace,\n message: 'Validation processed',\n data: { hasError, filesOptions, validator }\n });\n fieldsValidated = true;\n done();\n }\n\n busboy.on(\n 'file',\n (\n field: string,\n file: Readable,\n fileName: string,\n encoding: unknown,\n mimeType: string\n ) => {\n (async (): Promise<void> => {\n reportDebug({\n namespace,\n message: 'Found file in request',\n data: { hasError, field, fileName, encoding, mimeType, pendingWrites }\n });\n // Do not handle the stream if there has been an error previously\n if (hasError) file.destroy();\n pendingWrites++;\n // Find related file options based on the field name\n const options = filesOptions.find(\n (fileOption) => fileOption.key === field\n );\n // Make sure that the field is in the config\n if (!options) {\n triggerError(\n new WebError({\n statusCode: 400,\n errorCode: 'invalid-file-field',\n message: 'Request contains a file at an invalid field',\n hint: { field },\n data: { field }\n })\n );\n return;\n }\n reportDebug({\n namespace,\n message: 'Found options for file',\n data: {\n hasError,\n field,\n fileName,\n encoding,\n mimeType,\n pendingWrites,\n options\n }\n });\n // If a specific MIME type is required check it\n if (options.mimeTypes && !options.mimeTypes.includes(mimeType)) {\n triggerError(\n new WebError({\n statusCode: 415,\n errorCode: 'invalid-mime-type',\n message: 'Request contains a file with an invalid MIME type',\n hint: { field },\n data: { field, mimeType, expected: options.mimeTypes }\n })\n );\n return;\n }\n // Generate the destination path of the file and upload\n reportDebug({\n namespace,\n message: 'Starting file upload',\n data: {\n hasError,\n field,\n fileName,\n encoding,\n mimeType,\n pendingWrites,\n options\n }\n });\n try {\n const fileToUpload = options.image\n ? compressAvatar(file, options.image)\n : file;\n\n const uploadedFile = await Storage.upload(\n fileToUpload,\n options.bucket,\n {\n directory: options.path,\n mime: mimeType,\n noExtension: options.noExtension\n },\n {\n metadata: { contentType: mimeType }\n },\n { maxSize: options.maxSize }\n );\n\n reportDebug({\n namespace,\n message: 'File uploaded',\n data: {\n hasError,\n field,\n fileName,\n encoding,\n mimeType,\n pendingWrites,\n options,\n uploadedFile\n }\n });\n if (hasError) {\n // If some other failed before this one terminated\n // Delete file\n await Storage.delete(uploadedFile.bucket, uploadedFile.name);\n } else {\n // Handle successful upload\n // Add the file's data to the request\n pendingWrites--;\n if (!request.files) request.files = {};\n if (!request.files[field]) request.files[field] = [];\n request.files[field].push({\n ...uploadedFile,\n originalFileName: fileName\n });\n done();\n }\n } catch (error) {\n triggerError(error);\n }\n })().catch((error) => {\n triggerError(error);\n });\n }\n );\n\n /**\n * Handle non file fields\n * These fields will be pushed to the body as array of strings\n */\n busboy.on('field', (fieldName: string, value: unknown) => {\n if (!request.body) request.body = {};\n\n if (request.body[fieldName]) {\n request.body[fieldName].push(value);\n } else {\n request.body[fieldName] = [value];\n }\n });\n busboy.on('error', function (error: unknown) {\n triggerError(error);\n });\n busboy.on('finish', () => {\n (async (): Promise<void> => {\n reportDebug({\n namespace,\n message: 'Busboy finished reading',\n data: { hasError, filesOptions, validator, pendingWrites }\n });\n readFinished = true;\n await validateFields();\n done();\n })().catch((error) => {\n triggerError(error);\n });\n });\n return request.pipe(busboy);\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"fileUploader.js","names":["_WebError","require","_report","_multer","_interopRequireDefault","_validate","namespace","fileUploader","filesOptions","validator","request","response","next","reportDebug","message","data","storage","multer","memoryStorage","upload","limits","fileSize","any","err","error","uploadedFiles","Array","isArray","files","Object","values","flat","map","file","originalname","validate","uploadedFileKeys","fieldname","missingFiles","filter","option","required","includes","key","length","WebError","statusCode","errorCode"],"sources":["../../../src/middleware/fileUploader/fileUploader.ts"],"sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport { Schema } from 'express-validator';\nimport { WebError } from 'entities/WebError';\nimport { reportDebug } from 'utils/report';\nimport multer from 'multer';\nimport { FileUploaderConfig } from './fileUploader.types';\nimport { validate } from './validate';\n\nconst namespace = 'engine:middleware:fileUploader';\n\n/**\n * Middleware for handling file uploads.\n * @param filesOptions Configuration options for expected files.\n * @param validator Validation schema for request fields.\n */\nexport function fileUploader(\n filesOptions: Array<FileUploaderConfig>,\n validator: Schema\n) {\n return async (\n request: Request,\n response: Response,\n next: NextFunction\n ): Promise<void> => {\n reportDebug({\n namespace,\n message: 'Received request to upload file',\n data: { filesOptions, validator }\n });\n\n // Setup multer with memory storage\n const storage = multer.memoryStorage();\n const upload = multer({\n storage,\n limits: { fileSize: 10 * 1024 * 1024 } // Limit file size to 10MB\n }).any();\n\n upload(request, response, async (err) => {\n if (err) {\n reportDebug({\n namespace,\n message: 'Multer upload error',\n data: { error: err.message }\n });\n return next(err);\n }\n\n // Ensure `request.files` is properly typed\n let uploadedFiles: Express.Multer.File[] = [];\n if (Array.isArray(request.files)) {\n uploadedFiles = request.files;\n } else if (typeof request.files === 'object') {\n uploadedFiles = Object.values(request.files).flat();\n }\n\n // Log uploaded files\n reportDebug({\n namespace,\n message: 'Files Received',\n data: { uploadedFiles: uploadedFiles.map((file) => file.originalname) }\n });\n\n // Validate request body fields\n try {\n await validate(request, validator);\n } catch (error) {\n reportDebug({\n namespace,\n message: 'Validation failed',\n data: { error }\n });\n return next(error);\n }\n\n // Extract uploaded field names\n const uploadedFileKeys = uploadedFiles.map((file) => file.fieldname);\n\n // Check for missing required files\n const missingFiles = filesOptions.filter(\n (option) => option.required && !uploadedFileKeys.includes(option.key)\n );\n\n if (missingFiles.length > 0) {\n reportDebug({\n namespace,\n message: 'Missing required files',\n data: { missingFiles, uploadedFileKeys }\n });\n\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'missing-field',\n message: 'Some required files have not been uploaded',\n data: { missingFiles, uploadedFileKeys }\n })\n );\n }\n\n next();\n });\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,49 +1,30 @@
1
- import { CompressionOptions } from './compressAvatar';
2
- import { StorageUploadedFile } from '../../entities/Storage';
3
1
  declare global {
4
2
  namespace Express {
5
- /** Express request object */
6
3
  interface Request {
7
- /** Configuration for files that are uploaded with the request */
4
+ /** Uploaded files mapped by field name */
8
5
  files?: {
9
- [key: string]: Array<UploadedFile>;
10
- };
6
+ [key: string]: Express.Multer.File[];
7
+ } | Express.Multer.File[] | undefined;
11
8
  }
12
9
  }
13
10
  }
14
11
  /** Custom uploaded file metadata */
15
- export interface UploadedFile extends StorageUploadedFile {
16
- /** File name that was given to the file by the client */
12
+ export interface UploadedFile extends Express.Multer.File {
13
+ /** Original file name (use `originalname` from `Express.Multer.File`) */
17
14
  originalFileName: string;
15
+ /** File data buffer (use `buffer` from `Express.Multer.File`) */
16
+ file: Buffer;
18
17
  }
19
- /** Outputs setting for the file upload. Defines where a file should be stored and how it should be processed. */
20
- interface FileUploaderOutputs {
21
- /** Name of the output */
22
- name: string;
23
- /** Path to which this specific output is stored (defaults to default path if not set) */
24
- path?: string;
25
- /** Bucket to which this specific output is stored (defaults to default bucket if not set) */
26
- bucket?: string;
27
- }
28
- /** Configuration for a file upload */
18
+ /** Configuration for file upload */
29
19
  export interface FileUploaderConfig {
30
- /** Field key for which this configuration applies */
20
+ /** Field name expected in the request */
31
21
  key: string;
32
- /** A list of supported MIME Types for the file */
22
+ /** Allowed MIME types */
33
23
  mimeTypes?: Array<string>;
34
- /** Base path where the file should be stored (default used when no output config is set, will be used as temporary storage when outputs are set) */
35
- path?: string;
36
- /** Bucket in which the file should be stored (will be used as temporary storage when outputs are set) */
37
- bucket: string;
38
- /** Maximum size of the file (ex: 2MB) */
24
+ /** Maximum file size (e.g., "2MB") */
39
25
  maxSize?: string;
40
- /** Indicates that the file must be present */
26
+ /** Whether the file is required */
41
27
  required?: boolean;
42
- /** Do not store with an extension (will simply be a uuid) */
28
+ /** Whether to store without an extension */
43
29
  noExtension?: boolean;
44
- /** Specify multiple outputs with processing */
45
- outputs?: Array<FileUploaderOutputs>;
46
- /** @deprecated compression of images */
47
- image?: CompressionOptions;
48
30
  }
49
- export {};
@@ -1 +1 @@
1
- {"version":3,"file":"fileUploader.types.js","names":[],"sources":["../../../src/middleware/fileUploader/fileUploader.types.ts"],"sourcesContent":["import { CompressionOptions } from './compressAvatar';\nimport { StorageUploadedFile } from 'entities/Storage';\n\n// Override the Express request object to add the files\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n /** Express request object */\n interface Request {\n /** Configuration for files that are uploaded with the request */\n files?: { [key: string]: Array<UploadedFile> };\n }\n }\n}\n\n/** Custom uploaded file metadata */\nexport interface UploadedFile extends StorageUploadedFile {\n /** File name that was given to the file by the client */\n originalFileName: string;\n}\n\n/** Outputs setting for the file upload. Defines where a file should be stored and how it should be processed. */\ninterface FileUploaderOutputs {\n /** Name of the output */\n name: string;\n /** Path to which this specific output is stored (defaults to default path if not set) */\n path?: string;\n /** Bucket to which this specific output is stored (defaults to default bucket if not set) */\n bucket?: string;\n}\n\n/** Configuration for a file upload */\nexport interface FileUploaderConfig {\n /** Field key for which this configuration applies */\n key: string;\n /** A list of supported MIME Types for the file */\n mimeTypes?: Array<string>;\n /** Base path where the file should be stored (default used when no output config is set, will be used as temporary storage when outputs are set) */\n path?: string;\n /** Bucket in which the file should be stored (will be used as temporary storage when outputs are set) */\n bucket: string;\n /** Maximum size of the file (ex: 2MB) */\n maxSize?: string;\n /** Indicates that the file must be present */\n required?: boolean;\n /** Do not store with an extension (will simply be a uuid) */\n noExtension?: boolean;\n /** Specify multiple outputs with processing */\n outputs?: Array<FileUploaderOutputs>;\n /** @deprecated compression of images */\n image?: CompressionOptions;\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"fileUploader.types.js","names":[],"sources":["../../../src/middleware/fileUploader/fileUploader.types.ts"],"sourcesContent":["// Extend Express request object to include `files`\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** Uploaded files mapped by field name */\n files?:\n | { [key: string]: Express.Multer.File[] }\n | Express.Multer.File[]\n | undefined;\n }\n }\n}\n\n/** Custom uploaded file metadata */\nexport interface UploadedFile extends Express.Multer.File {\n /** Original file name (use `originalname` from `Express.Multer.File`) */\n originalFileName: string;\n /** File data buffer (use `buffer` from `Express.Multer.File`) */\n file: Buffer;\n}\n\n/** Configuration for file upload */\nexport interface FileUploaderConfig {\n /** Field name expected in the request */\n key: string;\n /** Allowed MIME types */\n mimeTypes?: Array<string>;\n /** Maximum file size (e.g., \"2MB\") */\n maxSize?: string;\n /** Whether the file is required */\n required?: boolean;\n /** Whether to store without an extension */\n noExtension?: boolean;\n}\n"],"mappings":"","ignoreList":[]}
@@ -6,66 +6,11 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.geminiFileUploader = geminiFileUploader;
8
8
  var _multer = _interopRequireDefault(require("multer"));
9
- var _server = require("@google/generative-ai/server");
10
- var _fs = _interopRequireDefault(require("fs"));
11
- var _path = _interopRequireDefault(require("path"));
12
- var _tmp = _interopRequireDefault(require("tmp"));
13
- var _report = require("../../utils/report");
9
+ var _utils = require("../../utils");
14
10
  var _checkEnvironment = require("../../utils/checkEnvironment");
15
11
  var _envAssert = require("../../utils/envAssert");
16
12
  const namespace = 'engine:middleware:geminiFileUploader';
17
- const apiKey = process.env.GOOGLE_AI_KEY ?? '';
18
- const fileManager = new _server.GoogleAIFileManager(apiKey);
19
13
  const storage = _multer.default.memoryStorage();
20
- async function uploadToGemini(buffer, mimeType, originalName) {
21
- // Add 'name' to the return type
22
- const tempFile = _tmp.default.fileSync({
23
- postfix: _path.default.extname(originalName)
24
- });
25
- _fs.default.writeFileSync(tempFile.name, buffer);
26
- const uploadResult = await fileManager.uploadFile(tempFile.name, {
27
- mimeType,
28
- displayName: originalName
29
- });
30
- tempFile.removeCallback();
31
- const file = uploadResult.file;
32
- (0, _report.reportDebug)({
33
- namespace,
34
- message: 'Uploaded file',
35
- data: {
36
- displayName: file.displayName,
37
- name: file.name
38
- }
39
- });
40
- return {
41
- name: file.name,
42
- uri: file.uri,
43
- mimeType: file.mimeType
44
- }; // Include 'name' in the return object
45
- }
46
- async function waitForFilesActive(files) {
47
- (0, _report.reportDebug)({
48
- namespace,
49
- message: 'Waiting for file processing...'
50
- });
51
- for (const {
52
- name
53
- } of files) {
54
- let file = await fileManager.getFile(name);
55
- while (file.state === 'PROCESSING') {
56
- process.stdout.write('.');
57
- await new Promise(resolve => setTimeout(resolve, 10_000));
58
- file = await fileManager.getFile(name);
59
- }
60
- if (file.state !== 'ACTIVE') {
61
- (0, _report.reportError)(`File ${file.name} failed to process`);
62
- }
63
- }
64
- (0, _report.reportDebug)({
65
- namespace,
66
- message: 'all files ready...\n'
67
- });
68
- }
69
14
  async function geminiFileUploader(req, res, next) {
70
15
  try {
71
16
  (0, _checkEnvironment.checkEnvironment)({
@@ -77,31 +22,38 @@ async function geminiFileUploader(req, res, next) {
77
22
  }).single('file');
78
23
  uploadSingle(req, res, async err => {
79
24
  if (err) {
80
- (0, _report.reportError)(`Error uploading file`);
25
+ (0, _utils.reportError)(`Error uploading file`);
81
26
  next(err);
82
27
  return;
83
28
  }
84
29
  const file = req.file;
85
30
  if (!file) {
86
- (0, _report.reportError)(`No file uploaded`);
31
+ (0, _utils.reportError)(`No file uploaded`);
87
32
  next(`No file uploaded`);
88
33
  return;
89
34
  }
90
35
  const mimeType = file.mimetype;
91
- const uploadedFile = await uploadToGemini(file.buffer, mimeType, file.originalname);
92
- await waitForFilesActive([uploadedFile]);
93
-
94
- // Set the fileUri and mimeType in the request object for the next middleware/route handler
95
- req.body.file = {
96
- fileUri: uploadedFile.uri,
97
- mimeType: uploadedFile.mimeType,
98
- originalname: file.originalname
99
- };
100
- next(); // Proceed to the next middleware or route handler
36
+ (0, _utils.reportDebug)({
37
+ namespace,
38
+ message: 'Upload to Gemini AI',
39
+ data: {
40
+ hasBuffer: !!file.buffer,
41
+ mimeType,
42
+ originalFileName: file.originalname
43
+ }
44
+ });
45
+ const fileResponse = await (0, _utils.geminiFileUpload)(file.buffer, mimeType, file.originalname);
46
+ (0, _utils.reportDebug)({
47
+ namespace,
48
+ message: 'File Uploaded to Gemini AI',
49
+ data: fileResponse
50
+ });
51
+ req.body.file = fileResponse;
52
+ next();
101
53
  });
102
54
  } catch (error) {
103
55
  console.error(error);
104
- (0, _report.reportError)(`Failed to upload or process file`);
56
+ (0, _utils.reportError)(`Failed to upload or process file`);
105
57
  next(error);
106
58
  }
107
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"geminiFileUploader.js","names":["_multer","_interopRequireDefault","require","_server","_fs","_path","_tmp","_report","_checkEnvironment","_envAssert","namespace","apiKey","process","env","GOOGLE_AI_KEY","fileManager","GoogleAIFileManager","storage","multer","memoryStorage","uploadToGemini","buffer","mimeType","originalName","tempFile","tmp","fileSync","postfix","path","extname","fs","writeFileSync","name","uploadResult","uploadFile","displayName","removeCallback","file","reportDebug","message","data","uri","waitForFilesActive","files","getFile","state","stdout","write","Promise","resolve","setTimeout","reportError","geminiFileUploader","req","res","next","checkEnvironment","envAssert","isString","uploadSingle","single","err","mimetype","uploadedFile","originalname","body","fileUri","error","console"],"sources":["../../../src/middleware/geminiFileUploader/geminiFileUploader.ts"],"sourcesContent":["import express from 'express';\nimport multer from 'multer';\nimport { GoogleAIFileManager } from '@google/generative-ai/server';\nimport fs from 'fs';\nimport path from 'path';\nimport tmp from 'tmp';\nimport { reportError, reportDebug } from 'utils/report';\nimport { checkEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\n\nconst namespace = 'engine:middleware:geminiFileUploader';\n\nconst apiKey = process.env.GOOGLE_AI_KEY ?? '';\nconst fileManager = new GoogleAIFileManager(apiKey);\n\nconst storage = multer.memoryStorage();\n\nasync function uploadToGemini(\n buffer: Buffer,\n mimeType: string,\n originalName: string\n): Promise<{ name: string; uri: string; mimeType: string }> {\n // Add 'name' to the return type\n const tempFile = tmp.fileSync({ postfix: path.extname(originalName) });\n\n fs.writeFileSync(tempFile.name, buffer);\n\n const uploadResult = await fileManager.uploadFile(tempFile.name, {\n mimeType,\n displayName: originalName\n });\n\n tempFile.removeCallback();\n\n const file = uploadResult.file;\n reportDebug({\n namespace,\n message: 'Uploaded file',\n data: { displayName: file.displayName, name: file.name }\n });\n return { name: file.name, uri: file.uri, mimeType: file.mimeType }; // Include 'name' in the return object\n}\n\nasync function waitForFilesActive(\n files: { name: string; uri: string; mimeType: string }[]\n): Promise<void> {\n reportDebug({\n namespace,\n message: 'Waiting for file processing...'\n });\n for (const { name } of files) {\n let file = await fileManager.getFile(name);\n while (file.state === 'PROCESSING') {\n process.stdout.write('.');\n await new Promise((resolve) => setTimeout(resolve, 10_000));\n file = await fileManager.getFile(name);\n }\n if (file.state !== 'ACTIVE') {\n reportError(`File ${file.name} failed to process`);\n }\n }\n reportDebug({\n namespace,\n message: 'all files ready...\\n'\n });\n}\n\nexport async function geminiFileUploader(\n req: express.Request,\n res: express.Response,\n next: express.NextFunction\n): Promise<void> {\n try {\n checkEnvironment({\n GOOGLE_AI_KEY: envAssert.isString()\n });\n // Use multer.single() inside the middleware\n const uploadSingle = multer({ storage }).single('file');\n\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError(`Error uploading file`);\n next(err);\n return;\n }\n\n const file = req.file;\n if (!file) {\n reportError(`No file uploaded`);\n next(`No file uploaded`);\n return;\n }\n const mimeType = file.mimetype;\n\n const uploadedFile = await uploadToGemini(\n file.buffer,\n mimeType,\n file.originalname\n );\n\n await waitForFilesActive([uploadedFile]);\n\n // Set the fileUri and mimeType in the request object for the next middleware/route handler\n req.body.file = {\n fileUri: uploadedFile.uri,\n mimeType: uploadedFile.mimeType,\n originalname: file.originalname\n };\n\n next(); // Proceed to the next middleware or route handler\n });\n } catch (error) {\n console.error(error);\n reportError(`Failed to upload or process file`);\n next(error);\n }\n}\n"],"mappings":";;;;;;;AACA,IAAAA,OAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,GAAA,GAAAH,sBAAA,CAAAC,OAAA;AACA,IAAAG,KAAA,GAAAJ,sBAAA,CAAAC,OAAA;AACA,IAAAI,IAAA,GAAAL,sBAAA,CAAAC,OAAA;AACA,IAAAK,OAAA,GAAAL,OAAA;AACA,IAAAM,iBAAA,GAAAN,OAAA;AACA,IAAAO,UAAA,GAAAP,OAAA;AAEA,MAAMQ,SAAS,GAAG,sCAAsC;AAExD,MAAMC,MAAM,GAAGC,OAAO,CAACC,GAAG,CAACC,aAAa,IAAI,EAAE;AAC9C,MAAMC,WAAW,GAAG,IAAIC,2BAAmB,CAACL,MAAM,CAAC;AAEnD,MAAMM,OAAO,GAAGC,eAAM,CAACC,aAAa,CAAC,CAAC;AAEtC,eAAeC,cAAcA,CAC3BC,MAAc,EACdC,QAAgB,EAChBC,YAAoB,EACsC;EAC1D;EACA,MAAMC,QAAQ,GAAGC,YAAG,CAACC,QAAQ,CAAC;IAAEC,OAAO,EAAEC,aAAI,CAACC,OAAO,CAACN,YAAY;EAAE,CAAC,CAAC;EAEtEO,WAAE,CAACC,aAAa,CAACP,QAAQ,CAACQ,IAAI,EAAEX,MAAM,CAAC;EAEvC,MAAMY,YAAY,GAAG,MAAMlB,WAAW,CAACmB,UAAU,CAACV,QAAQ,CAACQ,IAAI,EAAE;IAC/DV,QAAQ;IACRa,WAAW,EAAEZ;EACf,CAAC,CAAC;EAEFC,QAAQ,CAACY,cAAc,CAAC,CAAC;EAEzB,MAAMC,IAAI,GAAGJ,YAAY,CAACI,IAAI;EAC9B,IAAAC,mBAAW,EAAC;IACV5B,SAAS;IACT6B,OAAO,EAAE,eAAe;IACxBC,IAAI,EAAE;MAAEL,WAAW,EAAEE,IAAI,CAACF,WAAW;MAAEH,IAAI,EAAEK,IAAI,CAACL;IAAK;EACzD,CAAC,CAAC;EACF,OAAO;IAAEA,IAAI,EAAEK,IAAI,CAACL,IAAI;IAAES,GAAG,EAAEJ,IAAI,CAACI,GAAG;IAAEnB,QAAQ,EAAEe,IAAI,CAACf;EAAS,CAAC,CAAC,CAAC;AACtE;AAEA,eAAeoB,kBAAkBA,CAC/BC,KAAwD,EACzC;EACf,IAAAL,mBAAW,EAAC;IACV5B,SAAS;IACT6B,OAAO,EAAE;EACX,CAAC,CAAC;EACF,KAAK,MAAM;IAAEP;EAAK,CAAC,IAAIW,KAAK,EAAE;IAC5B,IAAIN,IAAI,GAAG,MAAMtB,WAAW,CAAC6B,OAAO,CAACZ,IAAI,CAAC;IAC1C,OAAOK,IAAI,CAACQ,KAAK,KAAK,YAAY,EAAE;MAClCjC,OAAO,CAACkC,MAAM,CAACC,KAAK,CAAC,GAAG,CAAC;MACzB,MAAM,IAAIC,OAAO,CAAEC,OAAO,IAAKC,UAAU,CAACD,OAAO,EAAE,MAAM,CAAC,CAAC;MAC3DZ,IAAI,GAAG,MAAMtB,WAAW,CAAC6B,OAAO,CAACZ,IAAI,CAAC;IACxC;IACA,IAAIK,IAAI,CAACQ,KAAK,KAAK,QAAQ,EAAE;MAC3B,IAAAM,mBAAW,EAAC,QAAQd,IAAI,CAACL,IAAI,oBAAoB,CAAC;IACpD;EACF;EACA,IAAAM,mBAAW,EAAC;IACV5B,SAAS;IACT6B,OAAO,EAAE;EACX,CAAC,CAAC;AACJ;AAEO,eAAea,kBAAkBA,CACtCC,GAAoB,EACpBC,GAAqB,EACrBC,IAA0B,EACX;EACf,IAAI;IACF,IAAAC,kCAAgB,EAAC;MACf1C,aAAa,EAAE2C,oBAAS,CAACC,QAAQ,CAAC;IACpC,CAAC,CAAC;IACF;IACA,MAAMC,YAAY,GAAG,IAAAzC,eAAM,EAAC;MAAED;IAAQ,CAAC,CAAC,CAAC2C,MAAM,CAAC,MAAM,CAAC;IAEvDD,YAAY,CAACN,GAAG,EAAEC,GAAG,EAAE,MAAOO,GAAG,IAAK;MACpC,IAAIA,GAAG,EAAE;QACP,IAAAV,mBAAW,EAAC,sBAAsB,CAAC;QACnCI,IAAI,CAACM,GAAG,CAAC;QACT;MACF;MAEA,MAAMxB,IAAI,GAAGgB,GAAG,CAAChB,IAAI;MACrB,IAAI,CAACA,IAAI,EAAE;QACT,IAAAc,mBAAW,EAAC,kBAAkB,CAAC;QAC/BI,IAAI,CAAC,kBAAkB,CAAC;QACxB;MACF;MACA,MAAMjC,QAAQ,GAAGe,IAAI,CAACyB,QAAQ;MAE9B,MAAMC,YAAY,GAAG,MAAM3C,cAAc,CACvCiB,IAAI,CAAChB,MAAM,EACXC,QAAQ,EACRe,IAAI,CAAC2B,YACP,CAAC;MAED,MAAMtB,kBAAkB,CAAC,CAACqB,YAAY,CAAC,CAAC;;MAExC;MACAV,GAAG,CAACY,IAAI,CAAC5B,IAAI,GAAG;QACd6B,OAAO,EAAEH,YAAY,CAACtB,GAAG;QACzBnB,QAAQ,EAAEyC,YAAY,CAACzC,QAAQ;QAC/B0C,YAAY,EAAE3B,IAAI,CAAC2B;MACrB,CAAC;MAEDT,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC;EACJ,CAAC,CAAC,OAAOY,KAAK,EAAE;IACdC,OAAO,CAACD,KAAK,CAACA,KAAK,CAAC;IACpB,IAAAhB,mBAAW,EAAC,kCAAkC,CAAC;IAC/CI,IAAI,CAACY,KAAK,CAAC;EACb;AACF","ignoreList":[]}
1
+ {"version":3,"file":"geminiFileUploader.js","names":["_multer","_interopRequireDefault","require","_utils","_checkEnvironment","_envAssert","namespace","storage","multer","memoryStorage","geminiFileUploader","req","res","next","checkEnvironment","GOOGLE_AI_KEY","envAssert","isString","uploadSingle","single","err","reportError","file","mimeType","mimetype","reportDebug","message","data","hasBuffer","buffer","originalFileName","originalname","fileResponse","geminiFileUpload","body","error","console"],"sources":["../../../src/middleware/geminiFileUploader/geminiFileUploader.ts"],"sourcesContent":["import express from 'express';\nimport multer from 'multer';\nimport { reportError, geminiFileUpload, reportDebug } from 'utils';\nimport { checkEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\n\nconst namespace = 'engine:middleware:geminiFileUploader';\nconst storage = multer.memoryStorage();\n\nexport async function geminiFileUploader(\n req: express.Request,\n res: express.Response,\n next: express.NextFunction\n): Promise<void> {\n try {\n checkEnvironment({\n GOOGLE_AI_KEY: envAssert.isString()\n });\n // Use multer.single() inside the middleware\n const uploadSingle = multer({ storage }).single('file');\n\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError(`Error uploading file`);\n next(err);\n return;\n }\n\n const file = req.file;\n if (!file) {\n reportError(`No file uploaded`);\n next(`No file uploaded`);\n return;\n }\n const mimeType = file.mimetype;\n\n reportDebug({\n namespace,\n message: 'Upload to Gemini AI',\n data: {\n hasBuffer: !!file.buffer,\n mimeType,\n originalFileName: file.originalname\n }\n });\n\n const fileResponse = await geminiFileUpload(\n file.buffer,\n mimeType,\n file.originalname\n );\n\n reportDebug({\n namespace,\n message: 'File Uploaded to Gemini AI',\n data: fileResponse\n });\n\n req.body.file = fileResponse;\n\n next();\n });\n } catch (error) {\n console.error(error);\n reportError(`Failed to upload or process file`);\n next(error);\n }\n}\n"],"mappings":";;;;;;;AACA,IAAAA,OAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AACA,IAAAE,iBAAA,GAAAF,OAAA;AACA,IAAAG,UAAA,GAAAH,OAAA;AAEA,MAAMI,SAAS,GAAG,sCAAsC;AACxD,MAAMC,OAAO,GAAGC,eAAM,CAACC,aAAa,CAAC,CAAC;AAE/B,eAAeC,kBAAkBA,CACtCC,GAAoB,EACpBC,GAAqB,EACrBC,IAA0B,EACX;EACf,IAAI;IACF,IAAAC,kCAAgB,EAAC;MACfC,aAAa,EAAEC,oBAAS,CAACC,QAAQ,CAAC;IACpC,CAAC,CAAC;IACF;IACA,MAAMC,YAAY,GAAG,IAAAV,eAAM,EAAC;MAAED;IAAQ,CAAC,CAAC,CAACY,MAAM,CAAC,MAAM,CAAC;IAEvDD,YAAY,CAACP,GAAG,EAAEC,GAAG,EAAE,MAAOQ,GAAG,IAAK;MACpC,IAAIA,GAAG,EAAE;QACP,IAAAC,kBAAW,EAAC,sBAAsB,CAAC;QACnCR,IAAI,CAACO,GAAG,CAAC;QACT;MACF;MAEA,MAAME,IAAI,GAAGX,GAAG,CAACW,IAAI;MACrB,IAAI,CAACA,IAAI,EAAE;QACT,IAAAD,kBAAW,EAAC,kBAAkB,CAAC;QAC/BR,IAAI,CAAC,kBAAkB,CAAC;QACxB;MACF;MACA,MAAMU,QAAQ,GAAGD,IAAI,CAACE,QAAQ;MAE9B,IAAAC,kBAAW,EAAC;QACVnB,SAAS;QACToB,OAAO,EAAE,qBAAqB;QAC9BC,IAAI,EAAE;UACJC,SAAS,EAAE,CAAC,CAACN,IAAI,CAACO,MAAM;UACxBN,QAAQ;UACRO,gBAAgB,EAAER,IAAI,CAACS;QACzB;MACF,CAAC,CAAC;MAEF,MAAMC,YAAY,GAAG,MAAM,IAAAC,uBAAgB,EACzCX,IAAI,CAACO,MAAM,EACXN,QAAQ,EACRD,IAAI,CAACS,YACP,CAAC;MAED,IAAAN,kBAAW,EAAC;QACVnB,SAAS;QACToB,OAAO,EAAE,4BAA4B;QACrCC,IAAI,EAAEK;MACR,CAAC,CAAC;MAEFrB,GAAG,CAACuB,IAAI,CAACZ,IAAI,GAAGU,YAAY;MAE5BnB,IAAI,CAAC,CAAC;IACR,CAAC,CAAC;EACJ,CAAC,CAAC,OAAOsB,KAAK,EAAE;IACdC,OAAO,CAACD,KAAK,CAACA,KAAK,CAAC;IACpB,IAAAd,kBAAW,EAAC,kCAAkC,CAAC;IAC/CR,IAAI,CAACsB,KAAK,CAAC;EACb;AACF","ignoreList":[]}
@@ -14,3 +14,4 @@ export * from './requestMetrics';
14
14
  export * from './version';
15
15
  export * from './geminiFileUploader';
16
16
  export * from './checkPermission';
17
+ export * from './multiPartFileUploader';