@eggjs/multipart 4.0.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +444 -0
  3. package/dist/commonjs/app/extend/context.d.ts +110 -0
  4. package/dist/commonjs/app/extend/context.js +279 -0
  5. package/dist/commonjs/app/middleware/multipart.d.ts +4 -0
  6. package/dist/commonjs/app/middleware/multipart.js +20 -0
  7. package/dist/commonjs/app/schedule/clean_tmpdir.d.ts +3 -0
  8. package/dist/commonjs/app/schedule/clean_tmpdir.js +58 -0
  9. package/dist/commonjs/app.d.ts +6 -0
  10. package/dist/commonjs/app.js +21 -0
  11. package/dist/commonjs/config/config.default.d.ts +98 -0
  12. package/dist/commonjs/config/config.default.js +31 -0
  13. package/dist/commonjs/index.d.ts +2 -0
  14. package/dist/commonjs/index.js +5 -0
  15. package/dist/commonjs/lib/LimitError.d.ts +5 -0
  16. package/dist/commonjs/lib/LimitError.js +16 -0
  17. package/dist/commonjs/lib/MultipartFileTooLargeError.d.ts +6 -0
  18. package/dist/commonjs/lib/MultipartFileTooLargeError.js +18 -0
  19. package/dist/commonjs/lib/utils.d.ts +4 -0
  20. package/dist/commonjs/lib/utils.js +93 -0
  21. package/dist/commonjs/package.json +3 -0
  22. package/dist/esm/app/extend/context.d.ts +110 -0
  23. package/dist/esm/app/extend/context.js +273 -0
  24. package/dist/esm/app/middleware/multipart.d.ts +4 -0
  25. package/dist/esm/app/middleware/multipart.js +18 -0
  26. package/dist/esm/app/schedule/clean_tmpdir.d.ts +3 -0
  27. package/dist/esm/app/schedule/clean_tmpdir.js +53 -0
  28. package/dist/esm/app.d.ts +6 -0
  29. package/dist/esm/app.js +18 -0
  30. package/dist/esm/config/config.default.d.ts +98 -0
  31. package/dist/esm/config/config.default.js +26 -0
  32. package/dist/esm/index.d.ts +2 -0
  33. package/dist/esm/index.js +3 -0
  34. package/dist/esm/lib/LimitError.d.ts +5 -0
  35. package/dist/esm/lib/LimitError.js +12 -0
  36. package/dist/esm/lib/MultipartFileTooLargeError.d.ts +6 -0
  37. package/dist/esm/lib/MultipartFileTooLargeError.js +14 -0
  38. package/dist/esm/lib/utils.d.ts +4 -0
  39. package/dist/esm/lib/utils.js +85 -0
  40. package/dist/esm/package.json +3 -0
  41. package/dist/package.json +4 -0
  42. package/package.json +103 -0
  43. package/src/app/extend/context.ts +368 -0
  44. package/src/app/middleware/multipart.ts +20 -0
  45. package/src/app/schedule/clean_tmpdir.ts +55 -0
  46. package/src/app.ts +20 -0
  47. package/src/config/config.default.ts +130 -0
  48. package/src/index.ts +2 -0
  49. package/src/lib/LimitError.ts +12 -0
  50. package/src/lib/MultipartFileTooLargeError.ts +14 -0
  51. package/src/lib/utils.ts +92 -0
  52. package/src/typings/index.d.ts +4 -0
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_assert_1 = __importDefault(require("node:assert"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_crypto_1 = require("node:crypto");
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_fs_1 = require("node:fs");
11
+ const node_stream_1 = require("node:stream");
12
+ const promises_2 = require("node:stream/promises");
13
+ // @ts-expect-error no types
14
+ const co_busboy_1 = __importDefault(require("co-busboy"));
15
+ const dayjs_1 = __importDefault(require("dayjs"));
16
+ const core_1 = require("@eggjs/core");
17
+ const utils_js_1 = require("../../lib/utils.js");
18
+ const LimitError_js_1 = require("../../lib/LimitError.js");
19
+ const MultipartFileTooLargeError_js_1 = require("../../lib/MultipartFileTooLargeError.js");
20
+ const HAS_CONSUMED = Symbol('Context#multipartHasConsumed');
21
+ class MultipartContext extends core_1.Context {
22
+ /**
23
+ * create multipart.parts instance, to get separated files.
24
+ * @function Context#multipart
25
+ * @param {Object} [options] - override default multipart configurations
26
+ * - {Boolean} options.autoFields
27
+ * - {String} options.defaultCharset
28
+ * - {String} options.defaultParamCharset
29
+ * - {Object} options.limits
30
+ * - {Function} options.checkFile
31
+ * @return {Yieldable | AsyncIterable<Yieldable>} parts
32
+ */
33
+ multipart(options = {}) {
34
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
35
+ const ctx = this;
36
+ // multipart/form-data
37
+ if (!ctx.is('multipart'))
38
+ ctx.throw(400, 'Content-Type must be multipart/*');
39
+ (0, node_assert_1.default)(!ctx[HAS_CONSUMED], 'the multipart request can\'t be consumed twice');
40
+ ctx[HAS_CONSUMED] = true;
41
+ const { autoFields, defaultCharset, defaultParamCharset, checkFile } = ctx.app.config.multipart;
42
+ const { fieldNameSize, fieldSize, fields, fileSize, files } = ctx.app.config.multipart;
43
+ options = extractOptions(options);
44
+ const parseOptions = Object.assign({
45
+ autoFields,
46
+ defCharset: defaultCharset,
47
+ defParamCharset: defaultParamCharset,
48
+ checkFile,
49
+ }, options);
50
+ // https://github.com/mscdex/busboy#busboy-methods
51
+ // merge limits
52
+ parseOptions.limits = Object.assign({
53
+ fieldNameSize,
54
+ fieldSize,
55
+ fields,
56
+ fileSize,
57
+ files,
58
+ }, options.limits);
59
+ // mount asyncIterator, so we can use `for await` to get parts
60
+ const parts = (0, co_busboy_1.default)(this, parseOptions);
61
+ parts[Symbol.asyncIterator] = async function* () {
62
+ let part;
63
+ do {
64
+ part = await parts();
65
+ if (!part)
66
+ continue;
67
+ if (Array.isArray(part)) {
68
+ if (part[3])
69
+ throw new LimitError_js_1.LimitError('Request_fieldSize_limit', 'Reach fieldSize limit');
70
+ // TODO: still not support at busboy 1.x (only support at urlencoded)
71
+ // https://github.com/mscdex/busboy/blob/v0.3.1/lib/types/multipart.js#L5
72
+ // https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js#L251
73
+ // if (part[2]) throw new LimitError('Request_fieldNameSize_limit', 'Reach fieldNameSize limit');
74
+ }
75
+ else {
76
+ // user click `upload` before choose a file, `part` will be file stream, but `part.filename` is empty must handler this, such as log error.
77
+ if (!part.filename) {
78
+ ctx.coreLogger.debug('[egg-multipart] file field `%s` is upload without file stream, will drop it.', part.fieldname);
79
+ await (0, promises_2.pipeline)(part, new node_stream_1.PassThrough());
80
+ continue;
81
+ }
82
+ // TODO: check whether filename is malicious input
83
+ // busboy only set truncated when consume the stream
84
+ if (part.truncated) {
85
+ // in case of emit 'limit' too fast
86
+ throw new LimitError_js_1.LimitError('Request_fileSize_limit', 'Reach fileSize limit');
87
+ }
88
+ else {
89
+ part.once('limit', function () {
90
+ this.emit('error', new LimitError_js_1.LimitError('Request_fileSize_limit', 'Reach fileSize limit'));
91
+ this.resume();
92
+ });
93
+ }
94
+ }
95
+ // dispatch part to outter logic such as for-await-of
96
+ yield part;
97
+ } while (part !== undefined);
98
+ };
99
+ return parts;
100
+ }
101
+ /**
102
+ * save request multipart data and files to `ctx.request`
103
+ * @function Context#saveRequestFiles
104
+ * @param {Object} options - { limits, checkFile, ... }
105
+ */
106
+ async saveRequestFiles(options = {}) {
107
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
108
+ const ctx = this;
109
+ const allowArrayField = ctx.app.config.multipart.allowArrayField;
110
+ let storeDir;
111
+ const requestBody = {};
112
+ const requestFiles = [];
113
+ options.autoFields = false;
114
+ const parts = ctx.multipart(options);
115
+ try {
116
+ for await (const part of parts) {
117
+ if (Array.isArray(part)) {
118
+ // fields
119
+ const [fieldName, fieldValue] = part;
120
+ if (!allowArrayField) {
121
+ requestBody[fieldName] = fieldValue;
122
+ }
123
+ else {
124
+ if (!requestBody[fieldName]) {
125
+ requestBody[fieldName] = fieldValue;
126
+ }
127
+ else if (!Array.isArray(requestBody[fieldName])) {
128
+ requestBody[fieldName] = [requestBody[fieldName], fieldValue];
129
+ }
130
+ else {
131
+ requestBody[fieldName].push(fieldValue);
132
+ }
133
+ }
134
+ }
135
+ else {
136
+ // stream
137
+ const { filename, fieldname, encoding, mime } = part;
138
+ if (!storeDir) {
139
+ // ${tmpdir}/YYYY/MM/DD/HH
140
+ storeDir = node_path_1.default.join(ctx.app.config.multipart.tmpdir, (0, dayjs_1.default)().format('YYYY/MM/DD/HH'));
141
+ await promises_1.default.mkdir(storeDir, { recursive: true });
142
+ }
143
+ // write to tmp file
144
+ const filepath = node_path_1.default.join(storeDir, (0, node_crypto_1.randomUUID)() + node_path_1.default.extname(filename));
145
+ const target = (0, node_fs_1.createWriteStream)(filepath);
146
+ await (0, promises_2.pipeline)(part, target);
147
+ const meta = {
148
+ filepath,
149
+ field: fieldname,
150
+ filename,
151
+ encoding,
152
+ mime,
153
+ // keep same property name as file stream, https://github.com/cojs/busboy/blob/master/index.js#L114
154
+ fieldname,
155
+ transferEncoding: encoding,
156
+ mimeType: mime,
157
+ };
158
+ requestFiles.push(meta);
159
+ }
160
+ }
161
+ }
162
+ catch (err) {
163
+ await ctx.cleanupRequestFiles(requestFiles);
164
+ throw err;
165
+ }
166
+ ctx.request.body = requestBody;
167
+ ctx.request.files = requestFiles;
168
+ }
169
+ /**
170
+ * get upload file stream
171
+ * @example
172
+ * ```js
173
+ * const stream = await ctx.getFileStream();
174
+ * // get other fields
175
+ * console.log(stream.fields);
176
+ * ```
177
+ * @function Context#getFileStream
178
+ * @param {Object} options
179
+ * - {Boolean} options.requireFile - required file submit, default is true
180
+ * - {String} options.defaultCharset
181
+ * - {String} options.defaultParamCharset
182
+ * - {Object} options.limits
183
+ * - {Function} options.checkFile
184
+ * @return {ReadStream} stream
185
+ * @since 1.0.0
186
+ * @deprecated Not safe enough, use `ctx.multipart()` instead
187
+ */
188
+ async getFileStream(options = {}) {
189
+ options.autoFields = true;
190
+ const parts = this.multipart(options);
191
+ let stream = await parts();
192
+ if (options.requireFile !== false) {
193
+ // stream not exists, treat as an exception
194
+ if (!stream || !stream.filename) {
195
+ this.throw(400, 'Can\'t found upload file');
196
+ }
197
+ }
198
+ if (!stream) {
199
+ stream = node_stream_1.Readable.from([]);
200
+ }
201
+ if (stream.truncated) {
202
+ throw new LimitError_js_1.LimitError('Request_fileSize_limit', 'Request file too large, please check multipart config');
203
+ }
204
+ stream.fields = parts.field;
205
+ stream.once('limit', () => {
206
+ const err = new MultipartFileTooLargeError_js_1.MultipartFileTooLargeError('Request file too large, please check multipart config', stream.fields, stream.filename);
207
+ if (stream.listenerCount('error') > 0) {
208
+ stream.emit('error', err);
209
+ this.coreLogger.warn(err);
210
+ }
211
+ else {
212
+ this.coreLogger.error(err);
213
+ // ignore next error event
214
+ stream.on('error', () => { });
215
+ }
216
+ // ignore all data
217
+ stream.resume();
218
+ });
219
+ return stream;
220
+ }
221
+ /**
222
+ * clean up request tmp files helper
223
+ * @function Context#cleanupRequestFiles
224
+ * @param {Array<String>} [files] - file paths need to cleanup, default is `ctx.request.files`.
225
+ */
226
+ async cleanupRequestFiles(files) {
227
+ if (!files || !files.length) {
228
+ files = this.request.files;
229
+ }
230
+ if (Array.isArray(files)) {
231
+ for (const file of files) {
232
+ try {
233
+ await promises_1.default.rm(file.filepath, { force: true, recursive: true });
234
+ }
235
+ catch (err) {
236
+ // warning log
237
+ this.coreLogger.warn('[egg-multipart-cleanupRequestFiles-error] file: %j, error: %s', file, err);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+ exports.default = MultipartContext;
244
+ function extractOptions(options = {}) {
245
+ const opts = {};
246
+ if (typeof options.autoFields === 'boolean') {
247
+ opts.autoFields = options.autoFields;
248
+ }
249
+ if (options.limits) {
250
+ opts.limits = options.limits;
251
+ }
252
+ if (options.checkFile) {
253
+ opts.checkFile = options.checkFile;
254
+ }
255
+ if (options.defCharset) {
256
+ opts.defCharset = options.defCharset;
257
+ }
258
+ if (options.defParamCharset) {
259
+ opts.defParamCharset = options.defParamCharset;
260
+ }
261
+ // compatible with config names
262
+ if (options.defaultCharset) {
263
+ opts.defCharset = options.defaultCharset;
264
+ }
265
+ if (options.defaultParamCharset) {
266
+ opts.defParamCharset = options.defaultParamCharset;
267
+ }
268
+ // limits
269
+ if (options.limits) {
270
+ const limits = opts.limits = { ...options.limits };
271
+ for (const key in limits) {
272
+ if (key.endsWith('Size') && limits[key]) {
273
+ limits[key] = (0, utils_js_1.humanizeBytes)(limits[key]);
274
+ }
275
+ }
276
+ }
277
+ return opts;
278
+ }
279
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcHAvZXh0ZW5kL2NvbnRleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw4REFBaUM7QUFDakMsMERBQTZCO0FBQzdCLDZDQUF5QztBQUN6QyxnRUFBa0M7QUFDbEMscUNBQTRDO0FBQzVDLDZDQUFvRDtBQUNwRCxtREFBZ0Q7QUFDaEQsNEJBQTRCO0FBQzVCLDBEQUE4QjtBQUM5QixrREFBMEI7QUFDMUIsc0NBQXNDO0FBQ3RDLGlEQUFtRDtBQUNuRCwyREFBcUQ7QUFDckQsMkZBQXFGO0FBRXJGLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0FBNEQ1RCxNQUFxQixnQkFBaUIsU0FBUSxjQUFPO0lBQ25EOzs7Ozs7Ozs7O09BVUc7SUFDSCxTQUFTLENBQUMsVUFBNEIsRUFBRTtRQUN0Qyw0REFBNEQ7UUFDNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLHNCQUFzQjtRQUN0QixJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUM7WUFBRSxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxrQ0FBa0MsQ0FBQyxDQUFDO1FBRTdFLElBQUEscUJBQU0sRUFBQyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsRUFBRSxnREFBZ0QsQ0FBQyxDQUFDO1FBQzdFLEdBQUcsQ0FBQyxZQUFZLENBQUMsR0FBRyxJQUFJLENBQUM7UUFFekIsTUFBTSxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsU0FBUyxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDO1FBQ2hHLE1BQU0sRUFBRSxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENB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
@@ -0,0 +1,4 @@
1
+ import type { Context, Next, EggCore } from '@eggjs/core';
2
+ import type { MultipartConfig } from '../../config/config.default.js';
3
+ declare const _default: (options: MultipartConfig, _app: EggCore) => (ctx: Context, next: Next) => Promise<void>;
4
+ export default _default;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const egg_path_matching_1 = require("egg-path-matching");
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ exports.default = (options, _app) => {
6
+ // normalize options
7
+ const matchFn = options.fileModeMatch && (0, egg_path_matching_1.pathMatching)({
8
+ match: options.fileModeMatch,
9
+ // pathToRegexpModule: app.options.pathToRegexpModule,
10
+ });
11
+ return async function multipart(ctx, next) {
12
+ if (!ctx.is('multipart'))
13
+ return next();
14
+ if (matchFn && !matchFn(ctx))
15
+ return next();
16
+ await ctx.saveRequestFiles();
17
+ return next();
18
+ };
19
+ };
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGlwYXJ0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2FwcC9taWRkbGV3YXJlL211bHRpcGFydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHlEQUFpRDtBQUlqRCw2REFBNkQ7QUFDN0Qsa0JBQWUsQ0FBQyxPQUF3QixFQUFFLElBQWEsRUFBRSxFQUFFO0lBQ3pELG9CQUFvQjtJQUNwQixNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsYUFBYSxJQUFJLElBQUEsZ0NBQVksRUFBQztRQUNwRCxLQUFLLEVBQUUsT0FBTyxDQUFDLGFBQWE7UUFDNUIsc0RBQXNEO0tBQ3ZELENBQUMsQ0FBQztJQUVILE9BQU8sS0FBSyxVQUFVLFNBQVMsQ0FBQyxHQUFZLEVBQUUsSUFBVTtRQUN0RCxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUM7WUFBRSxPQUFPLElBQUksRUFBRSxDQUFDO1FBQ3hDLElBQUksT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFFNUMsTUFBTSxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM3QixPQUFPLElBQUksRUFBRSxDQUFDO0lBQ2hCLENBQUMsQ0FBQztBQUNKLENBQUMsQ0FBQyJ9
@@ -0,0 +1,3 @@
1
+ import { EggCore } from '@eggjs/core';
2
+ declare const _default: (app: EggCore) => any;
3
+ export default _default;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const dayjs_1 = __importDefault(require("dayjs"));
9
+ exports.default = (app) => {
10
+ return class CleanTmpdir extends app.Subscription {
11
+ static get schedule() {
12
+ return {
13
+ type: 'worker',
14
+ cron: app.config.multipart.cleanSchedule.cron,
15
+ disable: app.config.multipart.cleanSchedule.disable,
16
+ immediate: false,
17
+ };
18
+ }
19
+ async _remove(dir) {
20
+ const { ctx } = this;
21
+ if (await promises_1.default.access(dir).then(() => true, () => false)) {
22
+ ctx.coreLogger.info('[@eggjs/multipart:CleanTmpdir] removing tmpdir: %j', dir);
23
+ try {
24
+ await promises_1.default.rm(dir, { force: true, recursive: true });
25
+ ctx.coreLogger.info('[@eggjs/multipart:CleanTmpdir:success] tmpdir: %j has been removed', dir);
26
+ }
27
+ catch (err) {
28
+ /* c8 ignore next 3 */
29
+ ctx.coreLogger.error('[@eggjs/multipart:CleanTmpdir:error] remove tmpdir: %j error: %s', dir, err);
30
+ ctx.coreLogger.error(err);
31
+ }
32
+ }
33
+ }
34
+ async subscribe() {
35
+ const { ctx } = this;
36
+ const config = ctx.app.config;
37
+ ctx.coreLogger.info('[@eggjs/multipart:CleanTmpdir] start clean tmpdir: %j', config.multipart.tmpdir);
38
+ // last year
39
+ const lastYear = (0, dayjs_1.default)().subtract(1, 'years');
40
+ const lastYearDir = node_path_1.default.join(config.multipart.tmpdir, lastYear.format('YYYY'));
41
+ await this._remove(lastYearDir);
42
+ // 3 months
43
+ for (let i = 1; i <= 3; i++) {
44
+ const date = (0, dayjs_1.default)().subtract(i, 'months');
45
+ const dir = node_path_1.default.join(config.multipart.tmpdir, date.format('YYYY/MM'));
46
+ await this._remove(dir);
47
+ }
48
+ // 7 days
49
+ for (let i = 1; i <= 7; i++) {
50
+ const date = (0, dayjs_1.default)().subtract(i, 'days');
51
+ const dir = node_path_1.default.join(config.multipart.tmpdir, date.format('YYYY/MM/DD'));
52
+ await this._remove(dir);
53
+ }
54
+ ctx.coreLogger.info('[@eggjs/multipart:CleanTmpdir] end');
55
+ }
56
+ };
57
+ };
58
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xlYW5fdG1wZGlyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2FwcC9zY2hlZHVsZS9jbGVhbl90bXBkaXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwwREFBNkI7QUFDN0IsZ0VBQWtDO0FBQ2xDLGtEQUEwQjtBQUcxQixrQkFBZSxDQUFDLEdBQVksRUFBTyxFQUFFO0lBQ25DLE9BQU8sTUFBTSxXQUFZLFNBQVEsR0FBRyxDQUFDLFlBQVk7UUFDL0MsTUFBTSxLQUFLLFFBQVE7WUFDakIsT0FBTztnQkFDTCxJQUFJLEVBQUUsUUFBUTtnQkFDZCxJQUFJLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDLElBQUk7Z0JBQzdDLE9BQU8sRUFBRSxHQUFHLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsT0FBTztnQkFDbkQsU0FBUyxFQUFFLEtBQUs7YUFDakIsQ0FBQztRQUNKLENBQUM7UUFFRCxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQVc7WUFDdkIsTUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztZQUNyQixJQUFJLE1BQU0sa0JBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUN2RCxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxvREFBb0QsRUFBRSxHQUFHLENBQUMsQ0FBQztnQkFDL0UsSUFBSSxDQUFDO29CQUNILE1BQU0sa0JBQUUsQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDbkQsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsb0VBQW9FLEVBQUUsR0FBRyxDQUFDLENBQUM7Z0JBQ2pHLENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixzQkFBc0I7b0JBQ3RCLEdBQUcsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLGtFQUFrRSxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztvQkFDbkcsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQzVCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELEtBQUssQ0FBQyxTQUFTO1lBQ2IsTUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztZQUNyQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQztZQUM5QixHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyx1REFBdUQsRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3RHLFlBQVk7WUFDWixNQUFNLFFBQVEsR0FBRyxJQUFBLGVBQUssR0FBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDOUMsTUFBTSxXQUFXLEdBQUcsbUJBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQ2hGLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUNoQyxXQUFXO1lBQ1gsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO2dCQUM1QixNQUFNLElBQUksR0FBRyxJQUFBLGVBQUssR0FBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQzNDLE1BQU0sR0FBRyxHQUFHLG1CQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztnQkFDdkUsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzFCLENBQUM7WUFDRCxTQUFTO1lBQ1QsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO2dCQUM1QixNQUFNLElBQUksR0FBRyxJQUFBLGVBQUssR0FBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQ3pDLE1BQU0sR0FBRyxHQUFHLG1CQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztnQkFDMUUsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzFCLENBQUM7WUFDRCxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDO1FBQzVELENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQyxDQUFDIn0=
@@ -0,0 +1,6 @@
1
+ import type { EggCore, ILifecycleBoot } from '@eggjs/core';
2
+ export default class AppBootHook implements ILifecycleBoot {
3
+ private app;
4
+ constructor(app: EggCore);
5
+ configWillLoad(): void;
6
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_js_1 = require("./lib/utils.js");
4
+ class AppBootHook {
5
+ app;
6
+ constructor(app) {
7
+ this.app = app;
8
+ }
9
+ configWillLoad() {
10
+ this.app.config.multipart = (0, utils_js_1.normalizeOptions)(this.app.config.multipart);
11
+ const options = this.app.config.multipart;
12
+ this.app.coreLogger.info('[@eggjs/multipart] %s mode enable', options.mode);
13
+ if (options.mode === 'file' || options.fileModeMatch) {
14
+ this.app.coreLogger.info('[@eggjs/multipart] will save temporary files to %j, cleanup job cron: %j', options.tmpdir, options.cleanSchedule.cron);
15
+ // enable multipart middleware
16
+ this.app.config.coreMiddleware.push('multipart');
17
+ }
18
+ }
19
+ }
20
+ exports.default = AppBootHook;
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUNBLDZDQUFrRDtBQUVsRCxNQUFxQixXQUFXO0lBQ1Y7SUFBcEIsWUFBb0IsR0FBWTtRQUFaLFFBQUcsR0FBSCxHQUFHLENBQVM7SUFBRyxDQUFDO0lBRXBDLGNBQWM7UUFDWixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEdBQUcsSUFBQSwyQkFBZ0IsRUFBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN4RSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFFMUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLG1DQUFtQyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM1RSxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTSxJQUFJLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNyRCxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsMEVBQTBFLEVBQ2pHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM5Qyw4QkFBOEI7WUFDOUIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNuRCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBZkQsOEJBZUMifQ==
@@ -0,0 +1,98 @@
1
+ import type { Context, EggAppInfo } from '@eggjs/core';
2
+ import type { PathMatchingPattern } from 'egg-path-matching';
3
+ export type MatchItem = string | RegExp | ((ctx: Context) => boolean);
4
+ /**
5
+ * multipart parser options
6
+ * @member Config#multipart
7
+ */
8
+ export interface MultipartConfig {
9
+ /**
10
+ * which mode to handle multipart request, default is `stream`, the hard way.
11
+ * If set mode to `file`, it's the easy way to handle multipart request and save it to local files.
12
+ * If you don't know the Node.js Stream work, maybe you should use the `file` mode to get started.
13
+ */
14
+ mode: 'stream' | 'file';
15
+ /**
16
+ * special url to use file mode when global `mode` is `stream`.
17
+ */
18
+ fileModeMatch?: PathMatchingPattern;
19
+ /**
20
+ * Auto set fields to parts, default is `false`.
21
+ * Only work on `stream` mode.
22
+ * If set true,all fields will be auto handle and can access by `parts.fields`
23
+ */
24
+ autoFields: boolean;
25
+ /**
26
+ * default charset encoding, don't change it before you real know about it
27
+ * Default is `utf8`
28
+ */
29
+ defaultCharset: string;
30
+ /**
31
+ * For multipart forms, the default character set to use for values of part header parameters (e.g. filename)
32
+ * that are not extended parameters (that contain an explicit charset), don't change it before you real know about it
33
+ * Default is `utf8`
34
+ */
35
+ defaultParamCharset: string;
36
+ /**
37
+ * Max field name size (in bytes), default is `100`
38
+ */
39
+ fieldNameSize: number;
40
+ /**
41
+ * Max field value size (in bytes), default is `100kb`
42
+ */
43
+ fieldSize: string | number;
44
+ /**
45
+ * Max number of non-file fields, default is `10`
46
+ */
47
+ fields: number;
48
+ /**
49
+ * Max file size (in bytes), default is `10mb`
50
+ */
51
+ fileSize: string | number;
52
+ /**
53
+ * Max number of file fields, default is `10`
54
+ */
55
+ files: number;
56
+ /**
57
+ * Add more ext file names to the `whitelist`, default is `[]`, only valid when `whitelist` is `null`
58
+ */
59
+ fileExtensions: string[];
60
+ /**
61
+ * The white ext file names, default is `null`
62
+ */
63
+ whitelist: string[] | ((filename: string) => boolean) | null;
64
+ /**
65
+ * Allow array field, default is `false`
66
+ */
67
+ allowArrayField: boolean;
68
+ /**
69
+ * The directory for temporary files. Only work on `file` mode.
70
+ * Default is `os.tmpdir()/egg-multipart-tmp/${appInfo.name}`
71
+ */
72
+ tmpdir: string;
73
+ /**
74
+ * The schedule for cleaning temporary files. Only work on `file` mode.
75
+ */
76
+ cleanSchedule: {
77
+ /**
78
+ * The cron expression for the schedule.
79
+ * Default is `0 30 4 * * *`
80
+ * @see https://github.com/eggjs/egg-schedule#cron-style-scheduling
81
+ */
82
+ cron: string;
83
+ /**
84
+ * Default is `false`
85
+ */
86
+ disable: boolean;
87
+ };
88
+ checkFile?(fieldname: string, file: any, filename: string, encoding: string, mimetype: string): void | Error;
89
+ }
90
+ declare const _default: (appInfo: EggAppInfo) => {
91
+ multipart: MultipartConfig;
92
+ };
93
+ export default _default;
94
+ declare module '@eggjs/core' {
95
+ interface EggAppConfig {
96
+ multipart: MultipartConfig;
97
+ }
98
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_os_1 = __importDefault(require("node:os"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ exports.default = (appInfo) => {
9
+ return {
10
+ multipart: {
11
+ mode: 'stream',
12
+ autoFields: false,
13
+ defaultCharset: 'utf8',
14
+ defaultParamCharset: 'utf8',
15
+ fieldNameSize: 100,
16
+ fieldSize: '100kb',
17
+ fields: 10,
18
+ fileSize: '10mb',
19
+ files: 10,
20
+ fileExtensions: [],
21
+ whitelist: null,
22
+ allowArrayField: false,
23
+ tmpdir: node_path_1.default.join(node_os_1.default.tmpdir(), 'egg-multipart-tmp', appInfo.name),
24
+ cleanSchedule: {
25
+ cron: '0 30 4 * * *',
26
+ disable: false,
27
+ },
28
+ },
29
+ };
30
+ };
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29uZmlnL2NvbmZpZy5kZWZhdWx0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsc0RBQXlCO0FBQ3pCLDBEQUE2QjtBQW1HN0Isa0JBQWUsQ0FBQyxPQUFtQixFQUFFLEVBQUU7SUFDckMsT0FBTztRQUNMLFNBQVMsRUFBRTtZQUNULElBQUksRUFBRSxRQUFRO1lBQ2QsVUFBVSxFQUFFLEtBQUs7WUFDakIsY0FBYyxFQUFFLE1BQU07WUFDdEIsbUJBQW1CLEVBQUUsTUFBTTtZQUMzQixhQUFhLEVBQUUsR0FBRztZQUNsQixTQUFTLEVBQUUsT0FBTztZQUNsQixNQUFNLEVBQUUsRUFBRTtZQUNWLFFBQVEsRUFBRSxNQUFNO1lBQ2hCLEtBQUssRUFBRSxFQUFFO1lBQ1QsY0FBYyxFQUFFLEVBQUU7WUFDbEIsU0FBUyxFQUFFLElBQUk7WUFDZixlQUFlLEVBQUUsS0FBSztZQUN0QixNQUFNLEVBQUUsbUJBQUksQ0FBQyxJQUFJLENBQUMsaUJBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxtQkFBbUIsRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQ2pFLGFBQWEsRUFBRTtnQkFDYixJQUFJLEVBQUUsY0FBYztnQkFDcEIsT0FBTyxFQUFFLEtBQUs7YUFDZjtTQUNpQjtLQUNyQixDQUFDO0FBQ0osQ0FBQyxDQUFDIn0=
@@ -0,0 +1,2 @@
1
+ import './config/config.default.js';
2
+ import './app/extend/context.js';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./config/config.default.js");
4
+ require("./app/extend/context.js");
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxzQ0FBb0M7QUFDcEMsbUNBQWlDIn0=
@@ -0,0 +1,5 @@
1
+ export declare class LimitError extends Error {
2
+ code: string;
3
+ status: number;
4
+ constructor(code: string, message: string);
5
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LimitError = void 0;
4
+ class LimitError extends Error {
5
+ code;
6
+ status;
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.code = code;
10
+ this.status = 413;
11
+ this.name = this.constructor.name;
12
+ Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ }
15
+ exports.LimitError = LimitError;
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTGltaXRFcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvTGltaXRFcnJvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxNQUFhLFVBQVcsU0FBUSxLQUFLO0lBQ25DLElBQUksQ0FBUztJQUNiLE1BQU0sQ0FBUztJQUVmLFlBQVksSUFBWSxFQUFFLE9BQWU7UUFDdkMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUM7UUFDbEIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQztRQUNsQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNsRCxDQUFDO0NBQ0Y7QUFYRCxnQ0FXQyJ9
@@ -0,0 +1,6 @@
1
+ export declare class MultipartFileTooLargeError extends Error {
2
+ status: number;
3
+ fields: Record<string, any>;
4
+ filename: string;
5
+ constructor(message: string, fields: Record<string, any>, filename: string);
6
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultipartFileTooLargeError = void 0;
4
+ class MultipartFileTooLargeError extends Error {
5
+ status;
6
+ fields;
7
+ filename;
8
+ constructor(message, fields, filename) {
9
+ super(message);
10
+ this.name = this.constructor.name;
11
+ this.status = 413;
12
+ this.fields = fields;
13
+ this.filename = filename;
14
+ Error.captureStackTrace(this, this.constructor);
15
+ }
16
+ }
17
+ exports.MultipartFileTooLargeError = MultipartFileTooLargeError;
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTXVsdGlwYXJ0RmlsZVRvb0xhcmdlRXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL011bHRpcGFydEZpbGVUb29MYXJnZUVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLE1BQWEsMEJBQTJCLFNBQVEsS0FBSztJQUNuRCxNQUFNLENBQVM7SUFDZixNQUFNLENBQXNCO0lBQzVCLFFBQVEsQ0FBUztJQUVqQixZQUFZLE9BQWUsRUFBRSxNQUEyQixFQUFFLFFBQWdCO1FBQ3hFLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUM7UUFDbEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUM7UUFDbEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsS0FBSyxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDbEQsQ0FBQztDQUNGO0FBYkQsZ0VBYUMifQ==
@@ -0,0 +1,4 @@
1
+ import { MultipartConfig } from '../config/config.default.js';
2
+ export declare const whitelist: string[];
3
+ export declare function humanizeBytes(size: number | string): number;
4
+ export declare function normalizeOptions(options: MultipartConfig): MultipartConfig;