node-server-engine 1.2.8 → 1.3.0
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 +4 -1
- package/README.md +128 -107
- package/dist/entities/Endpoint/Endpoint.d.ts +2 -0
- package/dist/entities/Endpoint/Endpoint.js +11 -2
- package/dist/entities/Endpoint/Endpoint.js.map +1 -1
- package/dist/entities/Endpoint/Endpoint.types.d.ts +3 -0
- package/dist/entities/Endpoint/Endpoint.types.js.map +1 -1
- package/dist/middleware/fileUploader/fileUploader.js +45 -3
- package/dist/middleware/fileUploader/fileUploader.js.map +1 -1
- package/dist/middleware/multiPartFileUploader/index.d.ts +1 -0
- package/dist/middleware/multiPartFileUploader/index.js +11 -0
- package/dist/middleware/multiPartFileUploader/index.js.map +1 -1
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.d.ts +4 -3
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js +169 -105
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js.map +1 -1
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.types.d.ts +20 -0
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.types.js +2 -0
- package/dist/middleware/multiPartFileUploader/multiPartFileUploader.types.js.map +1 -0
- package/dist/middleware/multiPartFileUploader/validate/index.d.ts +1 -0
- package/dist/middleware/multiPartFileUploader/validate/index.js +17 -0
- package/dist/middleware/multiPartFileUploader/validate/index.js.map +1 -0
- package/dist/middleware/multiPartFileUploader/validate/validate.d.ts +4 -0
- package/dist/middleware/multiPartFileUploader/validate/validate.js +28 -0
- package/dist/middleware/multiPartFileUploader/validate/validate.js.map +1 -0
- package/dist/utils/geminiFileUpload/geminiFileUpload.js +1 -5
- package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -1
- package/package.json +3 -1
- package/src/entities/Endpoint/Endpoint.ts +22 -3
- package/src/entities/Endpoint/Endpoint.types.ts +3 -0
- package/src/middleware/fileUploader/fileUploader.ts +51 -2
- package/src/middleware/multiPartFileUploader/index.ts +1 -0
- package/src/middleware/multiPartFileUploader/multiPartFileUploader.ts +193 -128
- package/src/middleware/multiPartFileUploader/multiPartFileUploader.types.ts +23 -0
- package/src/middleware/multiPartFileUploader/validate/index.ts +1 -0
- package/src/middleware/multiPartFileUploader/validate/validate.ts +27 -0
- package/src/utils/geminiFileUpload/geminiFileUpload.ts +1 -6
|
@@ -9,148 +9,212 @@ var _multer = _interopRequireDefault(require("multer"));
|
|
|
9
9
|
var _fsExtra = _interopRequireDefault(require("fs-extra"));
|
|
10
10
|
var _path = _interopRequireDefault(require("path"));
|
|
11
11
|
var _utils = require("../../utils");
|
|
12
|
-
|
|
12
|
+
var _WebError = require("../../entities/WebError");
|
|
13
|
+
var _validate = require("./validate");
|
|
13
14
|
const namespace = 'engine:middleware:multiPartFileUploader';
|
|
15
|
+
const TEMP_DIR = _path.default.join(process.env.FILE_STORAGE_FOLDER || './uploads', 'temp_chunks');
|
|
16
|
+
const FILE_DIR = _path.default.join(process.env.FILE_STORAGE_FOLDER || './uploads', 'completed_files');
|
|
17
|
+
_fsExtra.default.ensureDirSync(FILE_DIR);
|
|
14
18
|
_fsExtra.default.ensureDirSync(TEMP_DIR);
|
|
15
19
|
const storage = _multer.default.memoryStorage();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
function multiPartFileUploader(filesOption, validator) {
|
|
21
|
+
return async (req, res, next) => {
|
|
22
|
+
try {
|
|
23
|
+
(0, _utils.reportDebug)({
|
|
24
|
+
namespace,
|
|
25
|
+
message: 'Starting file upload middleware'
|
|
26
|
+
});
|
|
27
|
+
const uploadSingle = (0, _multer.default)({
|
|
28
|
+
storage
|
|
29
|
+
}).single('file');
|
|
30
|
+
uploadSingle(req, res, async err => {
|
|
31
|
+
if (err) {
|
|
32
|
+
(0, _utils.reportError)('Error uploading chunk');
|
|
33
|
+
return next(new _WebError.WebError({
|
|
34
|
+
statusCode: 500,
|
|
35
|
+
errorCode: 'upload-failed',
|
|
36
|
+
message: 'Error uploading file chunk',
|
|
37
|
+
data: {
|
|
38
|
+
error: err.message
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
(0, _utils.reportDebug)({
|
|
44
|
+
namespace,
|
|
45
|
+
message: 'Validating request'
|
|
46
|
+
});
|
|
47
|
+
await (0, _validate.validate)(req, validator);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
(0, _utils.reportDebug)({
|
|
50
|
+
namespace,
|
|
51
|
+
message: 'Validation failed',
|
|
52
|
+
data: {
|
|
53
|
+
error
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return next(new _WebError.WebError({
|
|
57
|
+
statusCode: 400,
|
|
58
|
+
errorCode: 'validation-failed',
|
|
59
|
+
message: 'Validation failed for file upload',
|
|
60
|
+
data: {
|
|
61
|
+
error: error.message
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
const {
|
|
66
|
+
filename,
|
|
67
|
+
totalChunks,
|
|
68
|
+
chunkIndex,
|
|
69
|
+
uniqueID
|
|
70
|
+
} = req.body;
|
|
71
|
+
(0, _utils.reportDebug)({
|
|
72
|
+
namespace,
|
|
73
|
+
message: 'Received chunk data',
|
|
74
|
+
data: {
|
|
37
75
|
filename,
|
|
38
76
|
totalChunks,
|
|
39
77
|
chunkIndex,
|
|
40
|
-
uniqueID
|
|
41
|
-
receivedChunk: !!req.file
|
|
78
|
+
uniqueID
|
|
42
79
|
}
|
|
43
|
-
};
|
|
44
|
-
(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
80
|
+
});
|
|
81
|
+
if (!filename || !totalChunks || chunkIndex === undefined || !uniqueID || !req.file) {
|
|
82
|
+
return next(new _WebError.WebError({
|
|
83
|
+
statusCode: 400,
|
|
84
|
+
errorCode: 'missing-field',
|
|
85
|
+
message: 'Missing required fields',
|
|
86
|
+
data: {
|
|
50
87
|
filename,
|
|
51
88
|
totalChunks,
|
|
52
89
|
chunkIndex,
|
|
53
|
-
uniqueID
|
|
90
|
+
uniqueID,
|
|
91
|
+
receivedChunk: !!req.file
|
|
54
92
|
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
return next();
|
|
58
|
-
}
|
|
59
|
-
const sessionFolder = _path.default.join(TEMP_DIR, uniqueID);
|
|
60
|
-
_fsExtra.default.ensureDirSync(sessionFolder);
|
|
61
|
-
const chunkPath = _path.default.join(sessionFolder, `${filename}.part${chunkIndex}`);
|
|
62
|
-
try {
|
|
63
|
-
await _fsExtra.default.writeFile(chunkPath, req.file.buffer);
|
|
64
|
-
} catch (writeErr) {
|
|
65
|
-
(0, _utils.reportError)(`Failed to write chunk ${chunkIndex}: ${writeErr.message}`);
|
|
66
|
-
return next(writeErr);
|
|
67
|
-
}
|
|
68
|
-
const uploadedChunks = await _fsExtra.default.readdir(sessionFolder);
|
|
69
|
-
if (uploadedChunks.length === parseInt(totalChunks, 10)) {
|
|
70
|
-
(0, _utils.reportDebug)({
|
|
71
|
-
namespace,
|
|
72
|
-
message: `All chunks uploaded. Merging file...`
|
|
73
|
-
});
|
|
74
|
-
try {
|
|
75
|
-
await mergeChunks(filename, totalChunks, sessionFolder);
|
|
76
|
-
} catch (mergeErr) {
|
|
77
|
-
(0, _utils.reportError)(`File merging failed: ${mergeErr.message}`);
|
|
78
|
-
return next(mergeErr);
|
|
93
|
+
}));
|
|
79
94
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
if (filesOption?.maxSize && req.file.size > parseInt(filesOption?.maxSize, 10)) {
|
|
96
|
+
return next(new _WebError.WebError({
|
|
97
|
+
statusCode: 400,
|
|
98
|
+
errorCode: 'chunk-too-large',
|
|
99
|
+
message: 'Uploaded chunk exceeds the maximum allowed size',
|
|
100
|
+
data: {
|
|
101
|
+
maxSize: filesOption.maxSize,
|
|
102
|
+
receivedSize: req.file.size
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
84
105
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
const sessionFolder = _path.default.join(TEMP_DIR, uniqueID);
|
|
107
|
+
_fsExtra.default.ensureDirSync(sessionFolder);
|
|
108
|
+
const chunkPath = _path.default.join(sessionFolder, `${filename}.part${chunkIndex}`);
|
|
109
|
+
try {
|
|
110
|
+
(0, _utils.reportDebug)({
|
|
111
|
+
namespace,
|
|
112
|
+
message: 'Writing chunk to disk',
|
|
113
|
+
data: {
|
|
114
|
+
chunkPath
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
await _fsExtra.default.writeFile(chunkPath, req.file.buffer);
|
|
118
|
+
} catch (writeErr) {
|
|
119
|
+
return next(new _WebError.WebError({
|
|
120
|
+
statusCode: 500,
|
|
121
|
+
errorCode: 'chunk-write-failed',
|
|
122
|
+
message: 'Failed to write chunk',
|
|
123
|
+
data: {
|
|
124
|
+
chunkIndex,
|
|
125
|
+
error: writeErr.message
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
const uploadedChunks = await _fsExtra.default.readdir(sessionFolder);
|
|
93
130
|
(0, _utils.reportDebug)({
|
|
94
131
|
namespace,
|
|
95
|
-
message:
|
|
132
|
+
message: 'Uploaded chunk count',
|
|
96
133
|
data: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
filename,
|
|
100
|
-
uniqueID
|
|
101
|
-
}
|
|
134
|
+
uploadedChunks: uploadedChunks.length,
|
|
135
|
+
totalChunks
|
|
102
136
|
}
|
|
103
137
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
138
|
+
if (uploadedChunks.length === parseInt(totalChunks, 10)) {
|
|
139
|
+
try {
|
|
140
|
+
const finalFilePath = _path.default.join(FILE_DIR, `${uniqueID}_${filename}`);
|
|
141
|
+
await mergeChunks(filename, totalChunks, sessionFolder, finalFilePath);
|
|
142
|
+
req.multipartFile = {
|
|
143
|
+
isPending: false,
|
|
144
|
+
originalname: filename,
|
|
145
|
+
uniqueID,
|
|
146
|
+
filePath: finalFilePath
|
|
147
|
+
};
|
|
148
|
+
} catch (mergeErr) {
|
|
149
|
+
return next(new _WebError.WebError({
|
|
150
|
+
statusCode: 500,
|
|
151
|
+
errorCode: 'file-merge-failed',
|
|
152
|
+
message: 'Error merging file chunks',
|
|
153
|
+
data: {
|
|
154
|
+
error: mergeErr.message
|
|
155
|
+
}
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
return next();
|
|
114
159
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
message: 'Chunk uploaded successfully',
|
|
119
|
-
details: {
|
|
120
|
-
filename,
|
|
160
|
+
req.multipartFile = {
|
|
161
|
+
isPending: true,
|
|
162
|
+
originalname: filename,
|
|
121
163
|
uniqueID,
|
|
122
164
|
chunkIndex,
|
|
123
165
|
totalChunks
|
|
166
|
+
};
|
|
167
|
+
next();
|
|
168
|
+
});
|
|
169
|
+
} catch (error) {
|
|
170
|
+
next(new _WebError.WebError({
|
|
171
|
+
statusCode: 500,
|
|
172
|
+
errorCode: 'unexpected-error',
|
|
173
|
+
message: 'Unexpected error in file upload',
|
|
174
|
+
data: {
|
|
175
|
+
error: error.message
|
|
124
176
|
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
} catch (error) {
|
|
129
|
-
(0, _utils.reportError)(`Failed to upload or process chunk: ${error.message}`);
|
|
130
|
-
next(error);
|
|
131
|
-
}
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
};
|
|
132
180
|
}
|
|
133
|
-
async function mergeChunks(filename, totalChunks, sessionFolder) {
|
|
134
|
-
const finalFilePath = _path.default.join(sessionFolder, filename);
|
|
181
|
+
async function mergeChunks(filename, totalChunks, sessionFolder, finalFilePath) {
|
|
135
182
|
const writeStream = _fsExtra.default.createWriteStream(finalFilePath);
|
|
183
|
+
(0, _utils.reportDebug)({
|
|
184
|
+
namespace,
|
|
185
|
+
message: 'Merging file chunks',
|
|
186
|
+
data: {
|
|
187
|
+
finalFilePath
|
|
188
|
+
}
|
|
189
|
+
});
|
|
136
190
|
for (let i = 0; i < parseInt(totalChunks, 10); i++) {
|
|
137
191
|
const partPath = _path.default.join(sessionFolder, `${filename}.part${i}`);
|
|
138
192
|
try {
|
|
139
193
|
const chunkData = await _fsExtra.default.readFile(partPath);
|
|
140
194
|
writeStream.write(chunkData);
|
|
141
195
|
} catch (error) {
|
|
142
|
-
|
|
143
|
-
|
|
196
|
+
throw new _WebError.WebError({
|
|
197
|
+
statusCode: 500,
|
|
198
|
+
errorCode: 'chunk-read-failed',
|
|
199
|
+
message: `Error reading chunk ${i}`,
|
|
200
|
+
data: {
|
|
201
|
+
error: error.message
|
|
202
|
+
}
|
|
203
|
+
});
|
|
144
204
|
}
|
|
145
205
|
_fsExtra.default.remove(partPath).catch(err => (0, _utils.reportError)(`Error removing part ${partPath}: ${err.message}`));
|
|
146
206
|
}
|
|
147
207
|
writeStream.end();
|
|
148
208
|
return new Promise((resolve, reject) => {
|
|
149
209
|
writeStream.on('finish', () => resolve(undefined));
|
|
150
|
-
writeStream.on('error', error => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
210
|
+
writeStream.on('error', error => reject(new _WebError.WebError({
|
|
211
|
+
statusCode: 500,
|
|
212
|
+
errorCode: 'file-merge-error',
|
|
213
|
+
message: 'Error merging file chunks',
|
|
214
|
+
data: {
|
|
215
|
+
error: error.message
|
|
216
|
+
}
|
|
217
|
+
})));
|
|
154
218
|
});
|
|
155
219
|
}
|
|
156
220
|
//# sourceMappingURL=multiPartFileUploader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiPartFileUploader.js","names":["_multer","_interopRequireDefault","require","_fsExtra","_path","_utils","TEMP_DIR","process","env","TEMP_CHUNK_FOLDER","namespace","fs","ensureDirSync","storage","multer","memoryStorage","multiPartFileUploader","req","res","next","uploadSingle","single","err","reportError","filename","totalChunks","chunkIndex","uniqueID","body","undefined","file","uploadDetails","status","message","details","receivedChunk","reportDebug","data","sessionFolder","path","join","chunkPath","writeFile","buffer","writeErr","uploadedChunks","readdir","length","parseInt","mergeChunks","mergeErr","finalFilePath","existsSync","Error","error","writeStream","createWriteStream","i","partPath","chunkData","readFile","write","remove","catch","end","Promise","resolve","reject","on"],"sources":["../../../src/middleware/multiPartFileUploader/multiPartFileUploader.ts"],"sourcesContent":["import express from 'express';\nimport multer from 'multer';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { reportDebug, reportError } from 'utils';\n\nconst TEMP_DIR: string =\n process.env.TEMP_CHUNK_FOLDER || './uploads/temp_chunks';\n\nconst namespace = 'engine:middleware:multiPartFileUploader';\n\nfs.ensureDirSync(TEMP_DIR);\n\nconst storage = multer.memoryStorage();\n\nasync function multiPartFileUploader(\n req: express.Request,\n res: express.Response,\n next: express.NextFunction\n): Promise<void> {\n try {\n const uploadSingle = multer({ storage }).single('file');\n\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError('Error uploading chunk');\n return next(err);\n }\n\n const { filename, totalChunks, chunkIndex, uniqueID } = req.body;\n\n if (\n !filename ||\n !totalChunks ||\n chunkIndex === undefined ||\n !uniqueID ||\n !req.file\n ) {\n req.body.uploadDetails = {\n status: 'failed',\n message: 'Missing required fields',\n details: {\n filename,\n totalChunks,\n chunkIndex,\n uniqueID,\n receivedChunk: !!req.file\n }\n };\n reportDebug({\n namespace,\n message: `Missing required fields`,\n data: {\n status: 'failed',\n details: {\n filename,\n totalChunks,\n chunkIndex,\n uniqueID\n }\n }\n });\n return next();\n }\n\n const sessionFolder = path.join(TEMP_DIR, uniqueID);\n fs.ensureDirSync(sessionFolder);\n\n const chunkPath = path.join(\n sessionFolder,\n `${filename}.part${chunkIndex}`\n );\n\n try {\n await fs.writeFile(chunkPath, req.file.buffer);\n } catch (writeErr) {\n reportError(\n `Failed to write chunk ${chunkIndex}: ${(writeErr as Error).message}`\n );\n return next(writeErr);\n }\n\n const uploadedChunks = await fs.readdir(sessionFolder);\n\n if (uploadedChunks.length === parseInt(totalChunks, 10)) {\n reportDebug({\n namespace,\n message: `All chunks uploaded. Merging file...`\n });\n\n try {\n await mergeChunks(filename, totalChunks, sessionFolder);\n } catch (mergeErr) {\n reportError(`File merging failed: ${(mergeErr as Error).message}`);\n return next(mergeErr);\n }\n\n const finalFilePath = path.join(sessionFolder, filename);\n\n if (!fs.existsSync(finalFilePath)) {\n reportError('File merging failed: File does not exist');\n return next(new Error('File merging failed'));\n }\n\n req.body.uploadDetails = {\n status: 'completed',\n message: 'File uploaded and merged successfully',\n details: { filename, uniqueID }\n };\n reportDebug({\n namespace,\n message: `File merged successfully`,\n data: {\n status: 'completed',\n details: { filename, uniqueID }\n }\n });\n\n return next();\n }\n\n reportDebug({\n namespace,\n message: `Chunk uploaded successfully`,\n data: {\n filename,\n uniqueID,\n chunkIndex,\n totalChunks\n }\n });\n\n req.body.uploadDetails = {\n status: 'pending',\n message: 'Chunk uploaded successfully',\n details: { filename, uniqueID, chunkIndex, totalChunks }\n };\n\n return next();\n });\n } catch (error) {\n reportError(\n `Failed to upload or process chunk: ${(error as Error).message}`\n );\n next(error);\n }\n}\n\nasync function mergeChunks(\n filename: string,\n totalChunks: string | number,\n sessionFolder: string\n): Promise<void> {\n const finalFilePath = path.join(sessionFolder, filename);\n const writeStream = fs.createWriteStream(finalFilePath);\n\n for (let i = 0; i < parseInt(totalChunks as string, 10); i++) {\n const partPath = path.join(sessionFolder, `${filename}.part${i}`);\n\n try {\n const chunkData = await fs.readFile(partPath);\n writeStream.write(chunkData);\n } catch (error) {\n reportError(`Error reading chunk ${i}: ${(error as Error).message}`);\n throw error;\n }\n\n fs.remove(partPath).catch((err: Error) =>\n reportError(`Error removing part ${partPath}: ${err.message}`)\n );\n }\n\n writeStream.end();\n\n return new Promise<void>((resolve, reject) => {\n writeStream.on('finish', () => resolve(undefined));\n writeStream.on('error', (error) => {\n reportError(`Error merging chunks: ${(error as Error).message}`);\n reject(error);\n });\n });\n}\n\nexport { multiPartFileUploader };\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"multiPartFileUploader.js","names":["_multer","_interopRequireDefault","require","_fsExtra","_path","_utils","_WebError","_validate","namespace","TEMP_DIR","path","join","process","env","FILE_STORAGE_FOLDER","FILE_DIR","fs","ensureDirSync","storage","multer","memoryStorage","multiPartFileUploader","filesOption","validator","req","res","next","reportDebug","message","uploadSingle","single","err","reportError","WebError","statusCode","errorCode","data","error","validate","filename","totalChunks","chunkIndex","uniqueID","body","undefined","file","receivedChunk","maxSize","size","parseInt","receivedSize","sessionFolder","chunkPath","writeFile","buffer","writeErr","uploadedChunks","readdir","length","finalFilePath","mergeChunks","multipartFile","isPending","originalname","filePath","mergeErr","writeStream","createWriteStream","i","partPath","chunkData","readFile","write","remove","catch","end","Promise","resolve","reject","on"],"sources":["../../../src/middleware/multiPartFileUploader/multiPartFileUploader.ts"],"sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport multer from 'multer';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { Schema } from 'express-validator';\nimport { reportDebug, reportError } from 'utils';\nimport { WebError } from 'entities/WebError';\nimport { validate } from './validate';\nimport { MultiPartFileUploaderConfig } from './multiPartFileUploader.types';\n\nconst namespace = 'engine:middleware:multiPartFileUploader';\n\nconst TEMP_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'temp_chunks'\n);\nconst FILE_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'completed_files'\n);\n\nfs.ensureDirSync(FILE_DIR);\nfs.ensureDirSync(TEMP_DIR);\n\nconst storage = multer.memoryStorage();\n\nexport function multiPartFileUploader(\n filesOption: MultiPartFileUploaderConfig,\n validator: Schema\n) {\n return async (\n req: Request,\n res: Response,\n next: NextFunction\n ): Promise<void> => {\n try {\n reportDebug({ namespace, message: 'Starting file upload middleware' });\n\n const uploadSingle = multer({ storage }).single('file');\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError('Error uploading chunk');\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'upload-failed',\n message: 'Error uploading file chunk',\n data: { error: err.message }\n })\n );\n }\n\n try {\n reportDebug({ namespace, message: 'Validating request' });\n await validate(req, validator);\n } catch (error) {\n reportDebug({\n namespace,\n message: 'Validation failed',\n data: { error }\n });\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'validation-failed',\n message: 'Validation failed for file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n\n const { filename, totalChunks, chunkIndex, uniqueID } = req.body;\n reportDebug({\n namespace,\n message: 'Received chunk data',\n data: { filename, totalChunks, chunkIndex, uniqueID }\n });\n\n if (\n !filename ||\n !totalChunks ||\n chunkIndex === undefined ||\n !uniqueID ||\n !req.file\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'missing-field',\n message: 'Missing required fields',\n data: {\n filename,\n totalChunks,\n chunkIndex,\n uniqueID,\n receivedChunk: !!req.file\n }\n })\n );\n }\n\n if (\n filesOption?.maxSize &&\n req.file.size > parseInt(filesOption?.maxSize, 10)\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'chunk-too-large',\n message: 'Uploaded chunk exceeds the maximum allowed size',\n data: {\n maxSize: filesOption.maxSize,\n receivedSize: req.file.size\n }\n })\n );\n }\n\n const sessionFolder = path.join(TEMP_DIR, uniqueID);\n fs.ensureDirSync(sessionFolder);\n const chunkPath = path.join(\n sessionFolder,\n `${filename}.part${chunkIndex}`\n );\n\n try {\n reportDebug({\n namespace,\n message: 'Writing chunk to disk',\n data: { chunkPath }\n });\n await fs.writeFile(chunkPath, req.file.buffer);\n } catch (writeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'chunk-write-failed',\n message: 'Failed to write chunk',\n data: { chunkIndex, error: (writeErr as Error).message }\n })\n );\n }\n\n const uploadedChunks = await fs.readdir(sessionFolder);\n reportDebug({\n namespace,\n message: 'Uploaded chunk count',\n data: { uploadedChunks: uploadedChunks.length, totalChunks }\n });\n\n if (uploadedChunks.length === parseInt(totalChunks, 10)) {\n try {\n const finalFilePath = path.join(\n FILE_DIR,\n `${uniqueID}_${filename}`\n );\n await mergeChunks(\n filename,\n totalChunks,\n sessionFolder,\n finalFilePath\n );\n req.multipartFile = {\n isPending: false,\n originalname: filename,\n uniqueID,\n filePath: finalFilePath\n };\n } catch (mergeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-failed',\n message: 'Error merging file chunks',\n data: { error: (mergeErr as Error).message }\n })\n );\n }\n return next();\n }\n\n req.multipartFile = {\n isPending: true,\n originalname: filename,\n uniqueID,\n chunkIndex,\n totalChunks\n };\n next();\n });\n } catch (error) {\n next(\n new WebError({\n statusCode: 500,\n errorCode: 'unexpected-error',\n message: 'Unexpected error in file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n };\n}\n\nasync function mergeChunks(\n filename: string,\n totalChunks: string | number,\n sessionFolder: string,\n finalFilePath: string\n): Promise<void> {\n const writeStream = fs.createWriteStream(finalFilePath);\n reportDebug({\n namespace,\n message: 'Merging file chunks',\n data: { finalFilePath }\n });\n\n for (let i = 0; i < parseInt(totalChunks as string, 10); i++) {\n const partPath = path.join(sessionFolder, `${filename}.part${i}`);\n try {\n const chunkData = await fs.readFile(partPath);\n writeStream.write(chunkData);\n } catch (error) {\n throw new WebError({\n statusCode: 500,\n errorCode: 'chunk-read-failed',\n message: `Error reading chunk ${i}`,\n data: { error: (error as Error).message }\n });\n }\n fs.remove(partPath).catch((err: Error) =>\n reportError(`Error removing part ${partPath}: ${err.message}`)\n );\n }\n\n writeStream.end();\n return new Promise<void>((resolve, reject) => {\n writeStream.on('finish', () => resolve(undefined));\n writeStream.on('error', (error) =>\n reject(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-error',\n message: 'Error merging file chunks',\n data: { error: (error as Error).message }\n })\n )\n );\n });\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
namespace Express {
|
|
3
|
+
interface Request {
|
|
4
|
+
/** Uploaded files mapped by field name */
|
|
5
|
+
multipartFile?: {
|
|
6
|
+
isPending: boolean;
|
|
7
|
+
originalname: string;
|
|
8
|
+
uniqueID: string;
|
|
9
|
+
filePath?: string;
|
|
10
|
+
chunkIndex?: number;
|
|
11
|
+
totalChunks?: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Configuration for Multi part file upload */
|
|
17
|
+
export interface MultiPartFileUploaderConfig {
|
|
18
|
+
required?: boolean;
|
|
19
|
+
maxSize?: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiPartFileUploader.types.js","names":[],"sources":["../../../src/middleware/multiPartFileUploader/multiPartFileUploader.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 multipartFile?: {\n isPending: boolean;\n originalname: string;\n uniqueID: string;\n filePath?: string;\n chunkIndex?: number;\n totalChunks?: number;\n };\n }\n }\n}\n\n/** Configuration for Multi part file upload */\nexport interface MultiPartFileUploaderConfig {\n required?: boolean;\n maxSize?: string;\n}\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './validate';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _validate = require("./validate");
|
|
7
|
+
Object.keys(_validate).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _validate[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _validate[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_validate","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get"],"sources":["../../../../src/middleware/multiPartFileUploader/validate/index.ts"],"sourcesContent":["export * from './validate';\n"],"mappings":";;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,SAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,SAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,SAAA,CAAAK,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.validate = validate;
|
|
7
|
+
var _validate = require("../../validate");
|
|
8
|
+
/** Validate a schema against a request using our validation middleware */
|
|
9
|
+
async function validate(request, schema) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
// Generate a validation chain using our middleware
|
|
12
|
+
const validationChain = (0, _validate.validate)(schema).flat();
|
|
13
|
+
if (!validationChain.length) resolve();
|
|
14
|
+
// Recursively go through each middleware by imitating the next function from express
|
|
15
|
+
let position = -1;
|
|
16
|
+
const next = error => {
|
|
17
|
+
if (error) reject(error);
|
|
18
|
+
position++;
|
|
19
|
+
if (position === validationChain.length) {
|
|
20
|
+
resolve();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
validationChain[position](request, undefined, next);
|
|
24
|
+
};
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","names":["_validate","require","validate","request","schema","Promise","resolve","reject","validationChain","validateMiddleware","flat","length","position","next","error","undefined"],"sources":["../../../../src/middleware/multiPartFileUploader/validate/validate.ts"],"sourcesContent":["import { Request } from 'express';\nimport { Schema } from 'express-validator';\nimport { validate as validateMiddleware } from 'middleware/validate';\n\n/** Validate a schema against a request using our validation middleware */\nexport async function validate(\n request: Request,\n schema: Schema\n): Promise<void> {\n return new Promise((resolve, reject) => {\n // Generate a validation chain using our middleware\n const validationChain = validateMiddleware(schema).flat();\n if (!validationChain.length) resolve();\n // Recursively go through each middleware by imitating the next function from express\n let position = -1;\n const next = (error?: unknown): void => {\n if (error) reject(error);\n position++;\n if (position === validationChain.length) {\n resolve();\n return;\n }\n validationChain[position](request, undefined, next);\n };\n next();\n });\n}\n"],"mappings":";;;;;;AAEA,IAAAA,SAAA,GAAAC,OAAA;AAEA;AACO,eAAeC,QAAQA,CAC5BC,OAAgB,EAChBC,MAAc,EACC;EACf,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtC;IACA,MAAMC,eAAe,GAAG,IAAAC,kBAAkB,EAACL,MAAM,CAAC,CAACM,IAAI,CAAC,CAAC;IACzD,IAAI,CAACF,eAAe,CAACG,MAAM,EAAEL,OAAO,CAAC,CAAC;IACtC;IACA,IAAIM,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAMC,IAAI,GAAIC,KAAe,IAAW;MACtC,IAAIA,KAAK,EAAEP,MAAM,CAACO,KAAK,CAAC;MACxBF,QAAQ,EAAE;MACV,IAAIA,QAAQ,KAAKJ,eAAe,CAACG,MAAM,EAAE;QACvCL,OAAO,CAAC,CAAC;QACT;MACF;MACAE,eAAe,CAACI,QAAQ,CAAC,CAACT,OAAO,EAAEY,SAAS,EAAEF,IAAI,CAAC;IACrD,CAAC;IACDA,IAAI,CAAC,CAAC;EACR,CAAC,CAAC;AACJ","ignoreList":[]}
|
|
@@ -11,11 +11,7 @@ var _path = _interopRequireDefault(require("path"));
|
|
|
11
11
|
var _tmp = _interopRequireDefault(require("tmp"));
|
|
12
12
|
var _report = require("../report");
|
|
13
13
|
const namespace = 'engine:utils:geminiFileUpload';
|
|
14
|
-
const
|
|
15
|
-
if (!apiKey) {
|
|
16
|
-
throw new Error('GOOGLE_AI_KEY is not set in environment variables');
|
|
17
|
-
}
|
|
18
|
-
const fileManager = new _server.GoogleAIFileManager(apiKey);
|
|
14
|
+
const fileManager = new _server.GoogleAIFileManager(process.env.GOOGLE_AI_KEY || '');
|
|
19
15
|
async function geminiFileUpload(buffer, mimeType, originalName) {
|
|
20
16
|
let tempFile;
|
|
21
17
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geminiFileUpload.js","names":["_server","require","_fs","_interopRequireDefault","_path","_tmp","_report","namespace","
|
|
1
|
+
{"version":3,"file":"geminiFileUpload.js","names":["_server","require","_fs","_interopRequireDefault","_path","_tmp","_report","namespace","fileManager","GoogleAIFileManager","process","env","GOOGLE_AI_KEY","geminiFileUpload","buffer","mimeType","originalName","tempFile","tmp","fileSync","postfix","path","extname","fs","writeFileSync","name","uploadResult","uploadFile","displayName","file","Error","reportDebug","message","data","geminiFile","getFile","state","stdout","write","Promise","resolve","setTimeout","errorMessage","reportError","success","error","originalname","fileUri","uri","removeCallback","cleanupError"],"sources":["../../../src/utils/geminiFileUpload/geminiFileUpload.ts"],"sourcesContent":["import { GoogleAIFileManager } from '@google/generative-ai/server';\nimport fs from 'fs';\nimport path from 'path';\nimport tmp from 'tmp';\nimport { reportError, reportDebug } from 'utils/report';\n\nconst namespace = 'engine:utils:geminiFileUpload';\n\nconst fileManager = new GoogleAIFileManager(process.env.GOOGLE_AI_KEY || '');\n\nexport async function geminiFileUpload(\n buffer: Buffer,\n mimeType: string,\n originalName: string\n): Promise<\n | { success: true; originalname: string; fileUri: string; mimeType: string }\n | { success: false; error: unknown }\n> {\n let tempFile;\n try {\n // 1. Create Temp File\n tempFile = tmp.fileSync({ postfix: path.extname(originalName) });\n fs.writeFileSync(tempFile.name, buffer);\n\n // 2. Upload File\n const uploadResult = await fileManager.uploadFile(tempFile.name, {\n mimeType,\n displayName: originalName\n });\n\n if (!uploadResult || !uploadResult.file) {\n throw new Error('Upload failed: No file returned');\n }\n\n const file = uploadResult.file;\n reportDebug({\n namespace,\n message: 'Uploaded file successfully',\n data: { displayName: file.displayName, name: file.name }\n });\n\n reportDebug({\n namespace,\n message: 'Waiting for file processing...'\n });\n\n // 3. Poll Until File is Processed\n let geminiFile = await fileManager.getFile(file.name);\n while (geminiFile?.state === 'PROCESSING') {\n process.stdout.write('.');\n await new Promise((resolve) => setTimeout(resolve, 10000));\n geminiFile = await fileManager.getFile(file.name);\n }\n\n if (!geminiFile || geminiFile.state !== 'ACTIVE') {\n const errorMessage = `File processing failed for ${file.name}`;\n reportError(errorMessage);\n return { success: false, error: new Error(errorMessage) };\n }\n\n reportDebug({\n namespace,\n message: 'File is ready'\n });\n\n return {\n success: true,\n originalname: file.name,\n fileUri: file.uri,\n mimeType: file.mimeType\n };\n } catch (error) {\n reportError({\n namespace,\n message: 'Error during Gemini file upload and processing',\n error\n });\n return { success: false, error };\n } finally {\n // Ensure cleanup of temp file\n if (tempFile) {\n try {\n tempFile.removeCallback();\n } catch (cleanupError) {\n reportError({\n namespace,\n message: 'Error cleaning up temp file',\n error: cleanupError\n });\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-server-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"backend-test-tools": "^1.0.7",
|
|
49
49
|
"body-parser": "^1.20.3",
|
|
50
50
|
"busboy": "^1.6.0",
|
|
51
|
+
"bytes": "^3.1.2",
|
|
51
52
|
"callsite": "^1.0.0",
|
|
52
53
|
"cors": "^2.8.5",
|
|
53
54
|
"device": "^0.2.0",
|
|
@@ -104,6 +105,7 @@
|
|
|
104
105
|
"@faker-js/faker": "^9.3.0",
|
|
105
106
|
"@google-cloud/precise-date": "^4.0.0",
|
|
106
107
|
"@types/busboy": "^1.5.4",
|
|
108
|
+
"@types/bytes": "^3.1.5",
|
|
107
109
|
"@types/callsite": "^1.0.34",
|
|
108
110
|
"@types/chai": "^5.0.1",
|
|
109
111
|
"@types/chai-as-promised": "^8.0.1",
|
|
@@ -24,8 +24,14 @@ import {
|
|
|
24
24
|
EndpointHandler
|
|
25
25
|
} from './Endpoint.types';
|
|
26
26
|
import { LogSeverity } from 'const';
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
validate,
|
|
29
|
+
fileUploader,
|
|
30
|
+
requestMetrics,
|
|
31
|
+
multiPartFileUploader
|
|
32
|
+
} from 'middleware';
|
|
28
33
|
import { FileUploaderConfig } from 'middleware/fileUploader';
|
|
34
|
+
import { MultiPartFileUploaderConfig } from 'middleware/multiPartFileUploader';
|
|
29
35
|
import { reportDebug, reportError, reportInfo } from 'utils/report';
|
|
30
36
|
|
|
31
37
|
const namespace = 'engine:Endpoint';
|
|
@@ -48,6 +54,8 @@ export class Endpoint<T extends EndpointAuthType> {
|
|
|
48
54
|
private authParams?: EndpointAuthParams<T>;
|
|
49
55
|
/** Settings to upload files through this endpoint (this will automatically switch the expected request to be a multipart) */
|
|
50
56
|
private files?: Array<FileUploaderConfig>;
|
|
57
|
+
/** Settings to upload file parts (Chunks) through this endpoint (this will automatically switch the expected request to be a multipart) */
|
|
58
|
+
private multiPartFile?: MultiPartFileUploaderConfig;
|
|
51
59
|
/** Express request handlers that should run on this endpoint before the business logic, they will run after the global middleware */
|
|
52
60
|
private middleware?: Array<Middleware<T>>;
|
|
53
61
|
/** Express error handlers that should be registered for this endpoint. they will run before the global error handler */
|
|
@@ -63,7 +71,9 @@ export class Endpoint<T extends EndpointAuthType> {
|
|
|
63
71
|
path: config.path,
|
|
64
72
|
authType: config.authType,
|
|
65
73
|
authParams: config.authParams,
|
|
66
|
-
file: config.file
|
|
74
|
+
file: config.file,
|
|
75
|
+
files: config.files,
|
|
76
|
+
multiPartFile: config.multiPartFile
|
|
67
77
|
}
|
|
68
78
|
});
|
|
69
79
|
|
|
@@ -113,10 +123,19 @@ export class Endpoint<T extends EndpointAuthType> {
|
|
|
113
123
|
// Register file upload
|
|
114
124
|
// Validation is included in the file upload process as this uses multipart/form-data
|
|
115
125
|
middlewareChain.push(fileUploader(this.files, this.validator));
|
|
116
|
-
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (this.multiPartFile) {
|
|
129
|
+
middlewareChain.push(
|
|
130
|
+
multiPartFileUploader(this.multiPartFile, this.validator)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!this.files && !this.multiPartFile) {
|
|
117
135
|
// Register validator for regular cases
|
|
118
136
|
middlewareChain.push(validate(this.validator));
|
|
119
137
|
}
|
|
138
|
+
|
|
120
139
|
// Register additional middleware
|
|
121
140
|
if (this.middleware) {
|
|
122
141
|
middlewareChain.push(...this.middleware);
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
authStatic,
|
|
18
18
|
FileUploaderConfig
|
|
19
19
|
} from 'middleware';
|
|
20
|
+
import { MultiPartFileUploaderConfig } from 'middleware/multiPartFileUploader/multiPartFileUploader.types';
|
|
20
21
|
|
|
21
22
|
export enum EndpointMethod {
|
|
22
23
|
GET = 'get',
|
|
@@ -129,6 +130,8 @@ export interface EndpointOptions<T extends EndpointAuthType> {
|
|
|
129
130
|
file?: FileUploaderConfig;
|
|
130
131
|
/** Settings to upload files through this endpoint (this will automatically switch the expected request to be a multipart) */
|
|
131
132
|
files?: Array<FileUploaderConfig>;
|
|
133
|
+
/** Settings to upload file parts (Chunks) through this endpoint (this will automatically switch the expected request to be a multipart) */
|
|
134
|
+
multiPartFile?: MultiPartFileUploaderConfig;
|
|
132
135
|
/** Express request handlers that should run on this endpoint before the business logic, they will run after the global middleware */
|
|
133
136
|
middleware?: MiddlewareChainElement<T>;
|
|
134
137
|
/** Express error handlers that should be registered for this endpoint. they will run before the global error handler */
|