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.
- package/.env.example +1 -4
- package/README.md +214 -18
- package/dist/entities/Server/Server.js +0 -10
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/middleware/fileUploader/fileUploader.d.ts +4 -5
- package/dist/middleware/fileUploader/fileUploader.js +68 -255
- package/dist/middleware/fileUploader/fileUploader.js.map +1 -1
- package/dist/middleware/fileUploader/fileUploader.types.d.ts +13 -32
- package/dist/middleware/fileUploader/fileUploader.types.js.map +1 -1
- package/dist/middleware/geminiFileUploader/geminiFileUploader.js +21 -69
- package/dist/middleware/geminiFileUploader/geminiFileUploader.js.map +1 -1
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/multiPartFileUploader/index.d.ts +1 -0
- package/dist/middleware/multiPartFileUploader/index.js +17 -0
- package/dist/middleware/multiPartFileUploader/index.js.map +1 -0
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.d.ts +3 -0
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js +156 -0
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js.map +1 -0
- package/dist/utils/createHttpServer/createHttpServer.js.map +1 -1
- package/dist/utils/geminiFileUpload/geminiFileUpload.d.ts +9 -0
- package/dist/utils/geminiFileUpload/geminiFileUpload.js +100 -0
- package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -0
- package/dist/utils/geminiFileUpload/index.d.ts +1 -0
- package/dist/utils/geminiFileUpload/index.js +17 -0
- package/dist/utils/geminiFileUpload/index.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +3 -1
- package/src/entities/Server/Server.ts +0 -6
- package/src/middleware/fileUploader/fileUploader.ts +70 -270
- package/src/middleware/fileUploader/fileUploader.types.ts +16 -33
- package/src/middleware/geminiFileUploader/geminiFileUploader.ts +19 -68
- package/src/middleware/index.ts +1 -0
- package/src/middleware/multiPartFileUploader/index.ts +1 -0
- package/src/middleware/multiPartFileUploader/multiPartFileUploader.ts +184 -0
- package/src/utils/createHttpServer/createHttpServer.ts +2 -3
- package/src/utils/geminiFileUpload/geminiFileUpload.ts +98 -0
- package/src/utils/geminiFileUpload/index.ts +1 -0
- package/src/utils/index.ts +3 -0
- package/test/env.ts +1 -0
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.d.ts +0 -6
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.js +0 -28
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.js.map +0 -1
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.d.ts +0 -12
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.js +0 -2
- package/dist/middleware/fileUploader/compressAvatar/compressAvatar.types.js.map +0 -1
- package/dist/middleware/fileUploader/compressAvatar/index.d.ts +0 -2
- package/dist/middleware/fileUploader/compressAvatar/index.js +0 -28
- package/dist/middleware/fileUploader/compressAvatar/index.js.map +0 -1
- package/src/middleware/fileUploader/compressAvatar/compressAvatar.ts +0 -25
- package/src/middleware/fileUploader/compressAvatar/compressAvatar.types.ts +0 -12
- package/src/middleware/fileUploader/compressAvatar/index.ts +0 -2
- package/src/middleware/fileUploader/fileUploader.test.ts +0 -310
|
@@ -1,303 +1,103 @@
|
|
|
1
|
-
import Busboy from 'busboy';
|
|
2
1
|
import { Request, Response, NextFunction } from 'express';
|
|
3
2
|
import { Schema } from 'express-validator';
|
|
4
|
-
import {
|
|
3
|
+
import { WebError } from 'entities/WebError';
|
|
4
|
+
import { reportDebug } from 'utils/report';
|
|
5
|
+
import multer from 'multer';
|
|
5
6
|
import { FileUploaderConfig } from './fileUploader.types';
|
|
6
|
-
import { isMultipart } from './isMultiPart';
|
|
7
7
|
import { validate } from './validate';
|
|
8
|
-
import { Storage } from 'entities/Storage';
|
|
9
|
-
import { WebError } from 'entities/WebError';
|
|
10
|
-
import { reportError, reportDebug } from 'utils/report';
|
|
11
|
-
import { Readable } from 'stream';
|
|
12
8
|
|
|
13
9
|
const namespace = 'engine:middleware:fileUploader';
|
|
14
10
|
|
|
15
11
|
/**
|
|
16
|
-
*
|
|
12
|
+
* Middleware for handling file uploads.
|
|
13
|
+
* @param filesOptions Configuration options for expected files.
|
|
14
|
+
* @param validator Validation schema for request fields.
|
|
17
15
|
*/
|
|
18
16
|
export function fileUploader(
|
|
19
|
-
|
|
17
|
+
filesOptions: Array<FileUploaderConfig>,
|
|
20
18
|
validator: Schema
|
|
21
19
|
) {
|
|
22
|
-
return (
|
|
20
|
+
return async (
|
|
23
21
|
request: Request,
|
|
24
22
|
response: Response,
|
|
25
23
|
next: NextFunction
|
|
26
|
-
):
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export function fileUploaderMiddleware(
|
|
32
|
-
request: Request,
|
|
33
|
-
response: Response,
|
|
34
|
-
next: NextFunction,
|
|
35
|
-
filesOptions: Array<FileUploaderConfig>,
|
|
36
|
-
validator: Schema
|
|
37
|
-
): Busboy.Busboy {
|
|
38
|
-
reportDebug({
|
|
39
|
-
namespace,
|
|
40
|
-
message: 'Received request to upload file',
|
|
41
|
-
data: { filesOptions, validator }
|
|
42
|
-
});
|
|
43
|
-
// Check that the request is a proper multipart request
|
|
44
|
-
if (!isMultipart(request))
|
|
45
|
-
throw new WebError({
|
|
46
|
-
statusCode: 415,
|
|
47
|
-
errorCode: 'invalid-content-type',
|
|
48
|
-
message: 'Request is not of type multipart/form-data',
|
|
49
|
-
data: { contentType: request.get('Content-Type') }
|
|
24
|
+
): Promise<void> => {
|
|
25
|
+
reportDebug({
|
|
26
|
+
namespace,
|
|
27
|
+
message: 'Received request to upload file',
|
|
28
|
+
data: { filesOptions, validator }
|
|
50
29
|
});
|
|
51
|
-
// If no files to handle ignore
|
|
52
|
-
if (!filesOptions.length) next();
|
|
53
|
-
// Parse multipart data with Busboy
|
|
54
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
-
const busboy = new (Busboy as any)({
|
|
56
|
-
headers: {
|
|
57
|
-
...request.headers,
|
|
58
|
-
'content-type': request.headers['content-type'] ?? ''
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
30
|
|
|
62
|
-
|
|
31
|
+
// Setup multer with memory storage
|
|
32
|
+
const storage = multer.memoryStorage();
|
|
33
|
+
const upload = multer({
|
|
34
|
+
storage,
|
|
35
|
+
limits: { fileSize: 10 * 1024 * 1024 } // Limit file size to 10MB
|
|
36
|
+
}).any();
|
|
63
37
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (hasError) return;
|
|
71
|
-
hasError = true;
|
|
72
|
-
request.unpipe(busboy);
|
|
73
|
-
request.on('readable', request.read.bind(request));
|
|
74
|
-
busboy.removeAllListeners();
|
|
75
|
-
next(error);
|
|
76
|
-
if (request.files) {
|
|
77
|
-
Object.values(request.files).forEach((files) => {
|
|
78
|
-
files.forEach((file) => {
|
|
79
|
-
Storage.delete(file.bucket, file.name).catch((error) => {
|
|
80
|
-
reportError(error);
|
|
81
|
-
});
|
|
38
|
+
upload(request, response, async (err) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
reportDebug({
|
|
41
|
+
namespace,
|
|
42
|
+
message: 'Multer upload error',
|
|
43
|
+
data: { error: err.message }
|
|
82
44
|
});
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let readFinished = false;
|
|
88
|
-
let fieldsValidated = false;
|
|
89
|
-
let pendingWrites = 0;
|
|
90
|
-
|
|
91
|
-
/** Function called when all the body has been parsed */
|
|
92
|
-
function done(): void {
|
|
93
|
-
if (readFinished && pendingWrites === 0 && fieldsValidated && !hasError) {
|
|
94
|
-
const uploadedFiles = Object.keys(request.files ?? {});
|
|
95
|
-
if (
|
|
96
|
-
filesOptions.some(
|
|
97
|
-
(options) => options.required && !uploadedFiles.includes(options.key)
|
|
98
|
-
)
|
|
99
|
-
) {
|
|
100
|
-
triggerError(
|
|
101
|
-
new WebError({
|
|
102
|
-
statusCode: 400,
|
|
103
|
-
errorCode: 'missing-field',
|
|
104
|
-
message: 'Some required files have not been uploaded',
|
|
105
|
-
data: { filesOptions, validator, uploadedFiles }
|
|
106
|
-
})
|
|
107
|
-
);
|
|
108
|
-
return;
|
|
45
|
+
return next(err);
|
|
109
46
|
}
|
|
110
|
-
next();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
47
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
48
|
+
// Ensure `request.files` is properly typed
|
|
49
|
+
let uploadedFiles: Express.Multer.File[] = [];
|
|
50
|
+
if (Array.isArray(request.files)) {
|
|
51
|
+
uploadedFiles = request.files;
|
|
52
|
+
} else if (typeof request.files === 'object') {
|
|
53
|
+
uploadedFiles = Object.values(request.files).flat();
|
|
54
|
+
}
|
|
120
55
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
} catch (error) {
|
|
128
|
-
triggerError(error);
|
|
129
|
-
}
|
|
130
|
-
reportDebug({
|
|
131
|
-
namespace,
|
|
132
|
-
message: 'Validation processed',
|
|
133
|
-
data: { hasError, filesOptions, validator }
|
|
134
|
-
});
|
|
135
|
-
fieldsValidated = true;
|
|
136
|
-
done();
|
|
137
|
-
}
|
|
56
|
+
// Log uploaded files
|
|
57
|
+
reportDebug({
|
|
58
|
+
namespace,
|
|
59
|
+
message: 'Files Received',
|
|
60
|
+
data: { uploadedFiles: uploadedFiles.map((file) => file.originalname) }
|
|
61
|
+
});
|
|
138
62
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
file: Readable,
|
|
144
|
-
fileName: string,
|
|
145
|
-
encoding: unknown,
|
|
146
|
-
mimeType: string
|
|
147
|
-
) => {
|
|
148
|
-
(async (): Promise<void> => {
|
|
149
|
-
reportDebug({
|
|
150
|
-
namespace,
|
|
151
|
-
message: 'Found file in request',
|
|
152
|
-
data: { hasError, field, fileName, encoding, mimeType, pendingWrites }
|
|
153
|
-
});
|
|
154
|
-
// Do not handle the stream if there has been an error previously
|
|
155
|
-
if (hasError) file.destroy();
|
|
156
|
-
pendingWrites++;
|
|
157
|
-
// Find related file options based on the field name
|
|
158
|
-
const options = filesOptions.find(
|
|
159
|
-
(fileOption) => fileOption.key === field
|
|
160
|
-
);
|
|
161
|
-
// Make sure that the field is in the config
|
|
162
|
-
if (!options) {
|
|
163
|
-
triggerError(
|
|
164
|
-
new WebError({
|
|
165
|
-
statusCode: 400,
|
|
166
|
-
errorCode: 'invalid-file-field',
|
|
167
|
-
message: 'Request contains a file at an invalid field',
|
|
168
|
-
hint: { field },
|
|
169
|
-
data: { field }
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
63
|
+
// Validate request body fields
|
|
64
|
+
try {
|
|
65
|
+
await validate(request, validator);
|
|
66
|
+
} catch (error) {
|
|
174
67
|
reportDebug({
|
|
175
68
|
namespace,
|
|
176
|
-
message: '
|
|
177
|
-
data: {
|
|
178
|
-
hasError,
|
|
179
|
-
field,
|
|
180
|
-
fileName,
|
|
181
|
-
encoding,
|
|
182
|
-
mimeType,
|
|
183
|
-
pendingWrites,
|
|
184
|
-
options
|
|
185
|
-
}
|
|
69
|
+
message: 'Validation failed',
|
|
70
|
+
data: { error }
|
|
186
71
|
});
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
// Generate the destination path of the file and upload
|
|
72
|
+
return next(error);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Extract uploaded field names
|
|
76
|
+
const uploadedFileKeys = uploadedFiles.map((file) => file.fieldname);
|
|
77
|
+
|
|
78
|
+
// Check for missing required files
|
|
79
|
+
const missingFiles = filesOptions.filter(
|
|
80
|
+
(option) => option.required && !uploadedFileKeys.includes(option.key)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (missingFiles.length > 0) {
|
|
201
84
|
reportDebug({
|
|
202
85
|
namespace,
|
|
203
|
-
message: '
|
|
204
|
-
data: {
|
|
205
|
-
hasError,
|
|
206
|
-
field,
|
|
207
|
-
fileName,
|
|
208
|
-
encoding,
|
|
209
|
-
mimeType,
|
|
210
|
-
pendingWrites,
|
|
211
|
-
options
|
|
212
|
-
}
|
|
86
|
+
message: 'Missing required files',
|
|
87
|
+
data: { missingFiles, uploadedFileKeys }
|
|
213
88
|
});
|
|
214
|
-
try {
|
|
215
|
-
const fileToUpload = options.image
|
|
216
|
-
? compressAvatar(file, options.image)
|
|
217
|
-
: file;
|
|
218
|
-
|
|
219
|
-
const uploadedFile = await Storage.upload(
|
|
220
|
-
fileToUpload,
|
|
221
|
-
options.bucket,
|
|
222
|
-
{
|
|
223
|
-
directory: options.path,
|
|
224
|
-
mime: mimeType,
|
|
225
|
-
noExtension: options.noExtension
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
metadata: { contentType: mimeType }
|
|
229
|
-
},
|
|
230
|
-
{ maxSize: options.maxSize }
|
|
231
|
-
);
|
|
232
89
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
pendingWrites,
|
|
243
|
-
options,
|
|
244
|
-
uploadedFile
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
if (hasError) {
|
|
248
|
-
// If some other failed before this one terminated
|
|
249
|
-
// Delete file
|
|
250
|
-
await Storage.delete(uploadedFile.bucket, uploadedFile.name);
|
|
251
|
-
} else {
|
|
252
|
-
// Handle successful upload
|
|
253
|
-
// Add the file's data to the request
|
|
254
|
-
pendingWrites--;
|
|
255
|
-
if (!request.files) request.files = {};
|
|
256
|
-
if (!request.files[field]) request.files[field] = [];
|
|
257
|
-
request.files[field].push({
|
|
258
|
-
...uploadedFile,
|
|
259
|
-
originalFileName: fileName
|
|
260
|
-
});
|
|
261
|
-
done();
|
|
262
|
-
}
|
|
263
|
-
} catch (error) {
|
|
264
|
-
triggerError(error);
|
|
265
|
-
}
|
|
266
|
-
})().catch((error) => {
|
|
267
|
-
triggerError(error);
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Handle non file fields
|
|
274
|
-
* These fields will be pushed to the body as array of strings
|
|
275
|
-
*/
|
|
276
|
-
busboy.on('field', (fieldName: string, value: unknown) => {
|
|
277
|
-
if (!request.body) request.body = {};
|
|
90
|
+
return next(
|
|
91
|
+
new WebError({
|
|
92
|
+
statusCode: 400,
|
|
93
|
+
errorCode: 'missing-field',
|
|
94
|
+
message: 'Some required files have not been uploaded',
|
|
95
|
+
data: { missingFiles, uploadedFileKeys }
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
}
|
|
278
99
|
|
|
279
|
-
|
|
280
|
-
request.body[fieldName].push(value);
|
|
281
|
-
} else {
|
|
282
|
-
request.body[fieldName] = [value];
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
busboy.on('error', function (error: unknown) {
|
|
286
|
-
triggerError(error);
|
|
287
|
-
});
|
|
288
|
-
busboy.on('finish', () => {
|
|
289
|
-
(async (): Promise<void> => {
|
|
290
|
-
reportDebug({
|
|
291
|
-
namespace,
|
|
292
|
-
message: 'Busboy finished reading',
|
|
293
|
-
data: { hasError, filesOptions, validator, pendingWrites }
|
|
294
|
-
});
|
|
295
|
-
readFinished = true;
|
|
296
|
-
await validateFields();
|
|
297
|
-
done();
|
|
298
|
-
})().catch((error) => {
|
|
299
|
-
triggerError(error);
|
|
100
|
+
next();
|
|
300
101
|
});
|
|
301
|
-
}
|
|
302
|
-
return request.pipe(busboy);
|
|
102
|
+
};
|
|
303
103
|
}
|
|
@@ -1,52 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import { StorageUploadedFile } from 'entities/Storage';
|
|
3
|
-
|
|
4
|
-
// Override the Express request object to add the files
|
|
1
|
+
// Extend Express request object to include `files`
|
|
5
2
|
declare global {
|
|
6
3
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
7
4
|
namespace Express {
|
|
8
|
-
/** Express request object */
|
|
9
5
|
interface Request {
|
|
10
|
-
/**
|
|
11
|
-
files?:
|
|
6
|
+
/** Uploaded files mapped by field name */
|
|
7
|
+
files?:
|
|
8
|
+
| { [key: string]: Express.Multer.File[] }
|
|
9
|
+
| Express.Multer.File[]
|
|
10
|
+
| undefined;
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
/** Custom uploaded file metadata */
|
|
17
|
-
export interface UploadedFile extends
|
|
18
|
-
/**
|
|
16
|
+
export interface UploadedFile extends Express.Multer.File {
|
|
17
|
+
/** Original file name (use `originalname` from `Express.Multer.File`) */
|
|
19
18
|
originalFileName: string;
|
|
19
|
+
/** File data buffer (use `buffer` from `Express.Multer.File`) */
|
|
20
|
+
file: Buffer;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
/**
|
|
23
|
-
interface FileUploaderOutputs {
|
|
24
|
-
/** Name of the output */
|
|
25
|
-
name: string;
|
|
26
|
-
/** Path to which this specific output is stored (defaults to default path if not set) */
|
|
27
|
-
path?: string;
|
|
28
|
-
/** Bucket to which this specific output is stored (defaults to default bucket if not set) */
|
|
29
|
-
bucket?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Configuration for a file upload */
|
|
23
|
+
/** Configuration for file upload */
|
|
33
24
|
export interface FileUploaderConfig {
|
|
34
|
-
/** Field
|
|
25
|
+
/** Field name expected in the request */
|
|
35
26
|
key: string;
|
|
36
|
-
/**
|
|
27
|
+
/** Allowed MIME types */
|
|
37
28
|
mimeTypes?: Array<string>;
|
|
38
|
-
/**
|
|
39
|
-
path?: string;
|
|
40
|
-
/** Bucket in which the file should be stored (will be used as temporary storage when outputs are set) */
|
|
41
|
-
bucket: string;
|
|
42
|
-
/** Maximum size of the file (ex: 2MB) */
|
|
29
|
+
/** Maximum file size (e.g., "2MB") */
|
|
43
30
|
maxSize?: string;
|
|
44
|
-
/**
|
|
31
|
+
/** Whether the file is required */
|
|
45
32
|
required?: boolean;
|
|
46
|
-
/**
|
|
33
|
+
/** Whether to store without an extension */
|
|
47
34
|
noExtension?: boolean;
|
|
48
|
-
/** Specify multiple outputs with processing */
|
|
49
|
-
outputs?: Array<FileUploaderOutputs>;
|
|
50
|
-
/** @deprecated compression of images */
|
|
51
|
-
image?: CompressionOptions;
|
|
52
35
|
}
|
|
@@ -1,70 +1,12 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import multer from 'multer';
|
|
3
|
-
import {
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import tmp from 'tmp';
|
|
7
|
-
import { reportError, reportDebug } from 'utils/report';
|
|
3
|
+
import { reportError, geminiFileUpload, reportDebug } from 'utils';
|
|
8
4
|
import { checkEnvironment } from 'utils/checkEnvironment';
|
|
9
5
|
import { envAssert } from 'utils/envAssert';
|
|
10
6
|
|
|
11
7
|
const namespace = 'engine:middleware:geminiFileUploader';
|
|
12
|
-
|
|
13
|
-
const apiKey = process.env.GOOGLE_AI_KEY ?? '';
|
|
14
|
-
const fileManager = new GoogleAIFileManager(apiKey);
|
|
15
|
-
|
|
16
8
|
const storage = multer.memoryStorage();
|
|
17
9
|
|
|
18
|
-
async function uploadToGemini(
|
|
19
|
-
buffer: Buffer,
|
|
20
|
-
mimeType: string,
|
|
21
|
-
originalName: string
|
|
22
|
-
): Promise<{ name: string; uri: string; mimeType: string }> {
|
|
23
|
-
// Add 'name' to the return type
|
|
24
|
-
const tempFile = tmp.fileSync({ postfix: path.extname(originalName) });
|
|
25
|
-
|
|
26
|
-
fs.writeFileSync(tempFile.name, buffer);
|
|
27
|
-
|
|
28
|
-
const uploadResult = await fileManager.uploadFile(tempFile.name, {
|
|
29
|
-
mimeType,
|
|
30
|
-
displayName: originalName
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
tempFile.removeCallback();
|
|
34
|
-
|
|
35
|
-
const file = uploadResult.file;
|
|
36
|
-
reportDebug({
|
|
37
|
-
namespace,
|
|
38
|
-
message: 'Uploaded file',
|
|
39
|
-
data: { displayName: file.displayName, name: file.name }
|
|
40
|
-
});
|
|
41
|
-
return { name: file.name, uri: file.uri, mimeType: file.mimeType }; // Include 'name' in the return object
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function waitForFilesActive(
|
|
45
|
-
files: { name: string; uri: string; mimeType: string }[]
|
|
46
|
-
): Promise<void> {
|
|
47
|
-
reportDebug({
|
|
48
|
-
namespace,
|
|
49
|
-
message: 'Waiting for file processing...'
|
|
50
|
-
});
|
|
51
|
-
for (const { name } of files) {
|
|
52
|
-
let file = await fileManager.getFile(name);
|
|
53
|
-
while (file.state === 'PROCESSING') {
|
|
54
|
-
process.stdout.write('.');
|
|
55
|
-
await new Promise((resolve) => setTimeout(resolve, 10_000));
|
|
56
|
-
file = await fileManager.getFile(name);
|
|
57
|
-
}
|
|
58
|
-
if (file.state !== 'ACTIVE') {
|
|
59
|
-
reportError(`File ${file.name} failed to process`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
reportDebug({
|
|
63
|
-
namespace,
|
|
64
|
-
message: 'all files ready...\n'
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
10
|
export async function geminiFileUploader(
|
|
69
11
|
req: express.Request,
|
|
70
12
|
res: express.Response,
|
|
@@ -92,22 +34,31 @@ export async function geminiFileUploader(
|
|
|
92
34
|
}
|
|
93
35
|
const mimeType = file.mimetype;
|
|
94
36
|
|
|
95
|
-
|
|
37
|
+
reportDebug({
|
|
38
|
+
namespace,
|
|
39
|
+
message: 'Upload to Gemini AI',
|
|
40
|
+
data: {
|
|
41
|
+
hasBuffer: !!file.buffer,
|
|
42
|
+
mimeType,
|
|
43
|
+
originalFileName: file.originalname
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const fileResponse = await geminiFileUpload(
|
|
96
48
|
file.buffer,
|
|
97
49
|
mimeType,
|
|
98
50
|
file.originalname
|
|
99
51
|
);
|
|
100
52
|
|
|
101
|
-
|
|
53
|
+
reportDebug({
|
|
54
|
+
namespace,
|
|
55
|
+
message: 'File Uploaded to Gemini AI',
|
|
56
|
+
data: fileResponse
|
|
57
|
+
});
|
|
102
58
|
|
|
103
|
-
|
|
104
|
-
req.body.file = {
|
|
105
|
-
fileUri: uploadedFile.uri,
|
|
106
|
-
mimeType: uploadedFile.mimeType,
|
|
107
|
-
originalname: file.originalname
|
|
108
|
-
};
|
|
59
|
+
req.body.file = fileResponse;
|
|
109
60
|
|
|
110
|
-
next();
|
|
61
|
+
next();
|
|
111
62
|
});
|
|
112
63
|
} catch (error) {
|
|
113
64
|
console.error(error);
|
package/src/middleware/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './multiPartFileUploader';
|