@kiyasov/platform-hono 1.0.7 → 1.0.9
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/.yarn/install-state.gz +0 -0
- package/Readme.md +0 -7
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/adapters/hono-adapter.d.ts +11 -11
- package/dist/cjs/src/adapters/hono-adapter.js +24 -24
- package/dist/cjs/src/adapters/hono-adapter.js.map +1 -1
- package/dist/cjs/src/drivers/constants/apollo.constants.d.ts +1 -0
- package/dist/cjs/src/drivers/constants/apollo.constants.js +5 -0
- package/dist/cjs/src/drivers/constants/apollo.constants.js.map +1 -0
- package/dist/cjs/src/drivers/constants/index.d.ts +1 -0
- package/dist/cjs/src/drivers/constants/index.js +18 -0
- package/dist/cjs/src/drivers/constants/index.js.map +1 -0
- package/dist/cjs/src/drivers/graphQLUpload/GraphQLUpload.d.ts +3 -0
- package/dist/cjs/src/drivers/graphQLUpload/GraphQLUpload.js +21 -0
- package/dist/cjs/src/drivers/graphQLUpload/GraphQLUpload.js.map +1 -0
- package/dist/cjs/src/drivers/graphQLUpload/Upload.d.ts +16 -0
- package/dist/cjs/src/drivers/graphQLUpload/Upload.js +17 -0
- package/dist/cjs/src/drivers/graphQLUpload/Upload.js.map +1 -0
- package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.d.ts +44 -0
- package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.js +190 -0
- package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.js.map +1 -0
- package/dist/cjs/src/drivers/graphQLUpload/index.d.ts +4 -0
- package/dist/cjs/src/drivers/graphQLUpload/index.js +21 -0
- package/dist/cjs/src/drivers/graphQLUpload/index.js.map +1 -0
- package/dist/cjs/src/drivers/graphQLUpload/processRequest.d.ts +2 -0
- package/dist/cjs/src/drivers/graphQLUpload/processRequest.js +45 -0
- package/dist/cjs/src/drivers/graphQLUpload/processRequest.js.map +1 -0
- package/dist/cjs/src/drivers/graphql.driver.d.ts +7 -0
- package/dist/cjs/src/drivers/graphql.driver.js +32 -5
- package/dist/cjs/src/drivers/graphql.driver.js.map +1 -1
- package/dist/cjs/src/drivers/index.d.ts +1 -0
- package/dist/cjs/src/drivers/index.js +1 -0
- package/dist/cjs/src/drivers/index.js.map +1 -1
- package/dist/cjs/src/drivers/services/plugins-explorer.service.d.ts +9 -0
- package/dist/cjs/src/drivers/services/plugins-explorer.service.js +25 -0
- package/dist/cjs/src/drivers/services/plugins-explorer.service.js.map +1 -0
- package/dist/cjs/src/drivers/services/processRequest.d.ts +2 -0
- package/dist/cjs/src/drivers/services/processRequest.js +45 -0
- package/dist/cjs/src/drivers/services/processRequest.js.map +1 -0
- package/dist/cjs/src/multer/crypto/index.d.ts +2 -0
- package/dist/cjs/src/multer/crypto/index.js +7 -0
- package/dist/cjs/src/multer/crypto/index.js.map +1 -0
- package/dist/cjs/src/multer/decorators/index.d.ts +2 -0
- package/dist/cjs/src/multer/decorators/index.js +19 -0
- package/dist/cjs/src/multer/decorators/index.js.map +1 -0
- package/dist/cjs/src/multer/decorators/uploaded-file-decorator.d.ts +1 -0
- package/dist/cjs/src/multer/decorators/uploaded-file-decorator.js +10 -0
- package/dist/cjs/src/multer/decorators/uploaded-file-decorator.js.map +1 -0
- package/dist/cjs/src/multer/decorators/uploaded-files-decorator.d.ts +1 -0
- package/dist/cjs/src/multer/decorators/uploaded-files-decorator.js +10 -0
- package/dist/cjs/src/multer/decorators/uploaded-files-decorator.js.map +1 -0
- package/dist/cjs/src/multer/fs/index.d.ts +2 -0
- package/dist/cjs/src/multer/fs/index.js +23 -0
- package/dist/cjs/src/multer/fs/index.js.map +1 -0
- package/dist/cjs/src/multer/index.d.ts +4 -0
- package/dist/cjs/src/multer/index.js +21 -0
- package/dist/cjs/src/multer/index.js.map +1 -0
- package/dist/cjs/src/multer/interceptors/any-files-interceptor.d.ts +3 -0
- package/dist/cjs/src/multer/interceptors/any-files-interceptor.js +27 -0
- package/dist/cjs/src/multer/interceptors/any-files-interceptor.js.map +1 -0
- package/dist/cjs/src/multer/interceptors/file-fields-interceptor.d.ts +4 -0
- package/dist/cjs/src/multer/interceptors/file-fields-interceptor.js +28 -0
- package/dist/cjs/src/multer/interceptors/file-fields-interceptor.js.map +1 -0
- package/dist/cjs/src/multer/interceptors/file-interceptor.d.ts +3 -0
- package/dist/cjs/src/multer/interceptors/file-interceptor.js +27 -0
- package/dist/cjs/src/multer/interceptors/file-interceptor.js.map +1 -0
- package/dist/cjs/src/multer/interceptors/files-interceptor.d.ts +3 -0
- package/dist/cjs/src/multer/interceptors/files-interceptor.js +27 -0
- package/dist/cjs/src/multer/interceptors/files-interceptor.js.map +1 -0
- package/dist/cjs/src/multer/interceptors/index.d.ts +4 -0
- package/dist/cjs/src/multer/interceptors/index.js +21 -0
- package/dist/cjs/src/multer/interceptors/index.js.map +1 -0
- package/dist/cjs/src/multer/multipart/exceptions.d.ts +1 -0
- package/dist/cjs/src/multer/multipart/exceptions.js +22 -0
- package/dist/cjs/src/multer/multipart/exceptions.js.map +1 -0
- package/dist/cjs/src/multer/multipart/file.d.ts +10 -0
- package/dist/cjs/src/multer/multipart/file.js +10 -0
- package/dist/cjs/src/multer/multipart/file.js.map +1 -0
- package/dist/cjs/src/multer/multipart/filter.d.ts +6 -0
- package/dist/cjs/src/multer/multipart/filter.js +22 -0
- package/dist/cjs/src/multer/multipart/filter.js.map +1 -0
- package/dist/cjs/src/multer/multipart/handlers/any-files.d.ts +8 -0
- package/dist/cjs/src/multer/multipart/handlers/any-files.js +33 -0
- package/dist/cjs/src/multer/multipart/handlers/any-files.js.map +1 -0
- package/dist/cjs/src/multer/multipart/handlers/file-fields.d.ts +14 -0
- package/dist/cjs/src/multer/multipart/handlers/file-fields.js +57 -0
- package/dist/cjs/src/multer/multipart/handlers/file-fields.js.map +1 -0
- package/dist/cjs/src/multer/multipart/handlers/index.d.ts +1 -0
- package/dist/cjs/src/multer/multipart/handlers/index.js +3 -0
- package/dist/cjs/src/multer/multipart/handlers/index.js.map +1 -0
- package/dist/cjs/src/multer/multipart/handlers/multiple-files.d.ts +8 -0
- package/dist/cjs/src/multer/multipart/handlers/multiple-files.js +40 -0
- package/dist/cjs/src/multer/multipart/handlers/multiple-files.js.map +1 -0
- package/dist/cjs/src/multer/multipart/handlers/single-file.d.ts +8 -0
- package/dist/cjs/src/multer/multipart/handlers/single-file.js +45 -0
- package/dist/cjs/src/multer/multipart/handlers/single-file.js.map +1 -0
- package/dist/cjs/src/multer/multipart/index.d.ts +3 -0
- package/dist/cjs/src/multer/multipart/index.js +19 -0
- package/dist/cjs/src/multer/multipart/index.js.map +1 -0
- package/dist/cjs/src/multer/multipart/options.d.ts +22 -0
- package/dist/cjs/src/multer/multipart/options.js +23 -0
- package/dist/cjs/src/multer/multipart/options.js.map +1 -0
- package/dist/cjs/src/multer/multipart/request.d.ts +16 -0
- package/dist/cjs/src/multer/multipart/request.js +25 -0
- package/dist/cjs/src/multer/multipart/request.js.map +1 -0
- package/dist/cjs/src/multer/storage/disk-storage.d.ts +31 -0
- package/dist/cjs/src/multer/storage/disk-storage.js +62 -0
- package/dist/cjs/src/multer/storage/disk-storage.js.map +1 -0
- package/dist/cjs/src/multer/storage/index.d.ts +3 -0
- package/dist/cjs/src/multer/storage/index.js +20 -0
- package/dist/cjs/src/multer/storage/index.js.map +1 -0
- package/dist/cjs/src/multer/storage/memory-storage.d.ts +17 -0
- package/dist/cjs/src/multer/storage/memory-storage.js +21 -0
- package/dist/cjs/src/multer/storage/memory-storage.js.map +1 -0
- package/dist/cjs/src/multer/storage/storage.d.ts +13 -0
- package/dist/cjs/src/multer/storage/storage.js +3 -0
- package/dist/cjs/src/multer/storage/storage.js.map +1 -0
- package/dist/cjs/src/multer/stream/index.d.ts +3 -0
- package/dist/cjs/src/multer/stream/index.js +7 -0
- package/dist/cjs/src/multer/stream/index.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/adapters/hono-adapter.d.ts +11 -11
- package/dist/esm/src/adapters/hono-adapter.js +35 -35
- package/dist/esm/src/adapters/hono-adapter.js.map +1 -1
- package/dist/esm/src/drivers/constants/apollo.constants.d.ts +1 -0
- package/dist/esm/src/drivers/constants/apollo.constants.js +2 -0
- package/dist/esm/src/drivers/constants/apollo.constants.js.map +1 -0
- package/dist/esm/src/drivers/constants/index.d.ts +1 -0
- package/dist/esm/src/drivers/constants/index.js +2 -0
- package/dist/esm/src/drivers/constants/index.js.map +1 -0
- package/dist/esm/src/drivers/graphQLUpload/GraphQLUpload.d.ts +3 -0
- package/dist/esm/src/drivers/graphQLUpload/GraphQLUpload.js +18 -0
- package/dist/esm/src/drivers/graphQLUpload/GraphQLUpload.js.map +1 -0
- package/dist/esm/src/drivers/graphQLUpload/Upload.d.ts +16 -0
- package/dist/esm/src/drivers/graphQLUpload/Upload.js +13 -0
- package/dist/esm/src/drivers/graphQLUpload/Upload.js.map +1 -0
- package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.d.ts +44 -0
- package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.js +183 -0
- package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.js.map +1 -0
- package/dist/esm/src/drivers/graphQLUpload/index.d.ts +4 -0
- package/dist/esm/src/drivers/graphQLUpload/index.js +5 -0
- package/dist/esm/src/drivers/graphQLUpload/index.js.map +1 -0
- package/dist/esm/src/drivers/graphQLUpload/processRequest.d.ts +2 -0
- package/dist/esm/src/drivers/graphQLUpload/processRequest.js +41 -0
- package/dist/esm/src/drivers/graphQLUpload/processRequest.js.map +1 -0
- package/dist/esm/src/drivers/graphql.driver.d.ts +7 -0
- package/dist/esm/src/drivers/graphql.driver.js +33 -6
- package/dist/esm/src/drivers/graphql.driver.js.map +1 -1
- package/dist/esm/src/drivers/index.d.ts +1 -0
- package/dist/esm/src/drivers/index.js +1 -0
- package/dist/esm/src/drivers/index.js.map +1 -1
- package/dist/esm/src/drivers/services/plugins-explorer.service.d.ts +9 -0
- package/dist/esm/src/drivers/services/plugins-explorer.service.js +21 -0
- package/dist/esm/src/drivers/services/plugins-explorer.service.js.map +1 -0
- package/dist/esm/src/drivers/services/processRequest.d.ts +2 -0
- package/dist/esm/src/drivers/services/processRequest.js +41 -0
- package/dist/esm/src/drivers/services/processRequest.js.map +1 -0
- package/dist/esm/src/multer/crypto/index.d.ts +2 -0
- package/dist/esm/src/multer/crypto/index.js +4 -0
- package/dist/esm/src/multer/crypto/index.js.map +1 -0
- package/dist/esm/src/multer/decorators/index.d.ts +2 -0
- package/dist/esm/src/multer/decorators/index.js +3 -0
- package/dist/esm/src/multer/decorators/index.js.map +1 -0
- package/dist/esm/src/multer/decorators/uploaded-file-decorator.d.ts +1 -0
- package/dist/esm/src/multer/decorators/uploaded-file-decorator.js +7 -0
- package/dist/esm/src/multer/decorators/uploaded-file-decorator.js.map +1 -0
- package/dist/esm/src/multer/decorators/uploaded-files-decorator.d.ts +1 -0
- package/dist/esm/src/multer/decorators/uploaded-files-decorator.js +7 -0
- package/dist/esm/src/multer/decorators/uploaded-files-decorator.js.map +1 -0
- package/dist/esm/src/multer/fs/index.d.ts +2 -0
- package/dist/esm/src/multer/fs/index.js +18 -0
- package/dist/esm/src/multer/fs/index.js.map +1 -0
- package/dist/esm/src/multer/index.d.ts +4 -0
- package/dist/esm/src/multer/index.js +5 -0
- package/dist/esm/src/multer/index.js.map +1 -0
- package/dist/esm/src/multer/interceptors/any-files-interceptor.d.ts +3 -0
- package/dist/esm/src/multer/interceptors/any-files-interceptor.js +23 -0
- package/dist/esm/src/multer/interceptors/any-files-interceptor.js.map +1 -0
- package/dist/esm/src/multer/interceptors/file-fields-interceptor.d.ts +4 -0
- package/dist/esm/src/multer/interceptors/file-fields-interceptor.js +24 -0
- package/dist/esm/src/multer/interceptors/file-fields-interceptor.js.map +1 -0
- package/dist/esm/src/multer/interceptors/file-interceptor.d.ts +3 -0
- package/dist/esm/src/multer/interceptors/file-interceptor.js +23 -0
- package/dist/esm/src/multer/interceptors/file-interceptor.js.map +1 -0
- package/dist/esm/src/multer/interceptors/files-interceptor.d.ts +3 -0
- package/dist/esm/src/multer/interceptors/files-interceptor.js +23 -0
- package/dist/esm/src/multer/interceptors/files-interceptor.js.map +1 -0
- package/dist/esm/src/multer/interceptors/index.d.ts +4 -0
- package/dist/esm/src/multer/interceptors/index.js +5 -0
- package/dist/esm/src/multer/interceptors/index.js.map +1 -0
- package/dist/esm/src/multer/multipart/exceptions.d.ts +1 -0
- package/dist/esm/src/multer/multipart/exceptions.js +18 -0
- package/dist/esm/src/multer/multipart/exceptions.js.map +1 -0
- package/dist/esm/src/multer/multipart/file.d.ts +10 -0
- package/dist/esm/src/multer/multipart/file.js +6 -0
- package/dist/esm/src/multer/multipart/file.js.map +1 -0
- package/dist/esm/src/multer/multipart/filter.d.ts +6 -0
- package/dist/esm/src/multer/multipart/filter.js +18 -0
- package/dist/esm/src/multer/multipart/filter.js.map +1 -0
- package/dist/esm/src/multer/multipart/handlers/any-files.d.ts +8 -0
- package/dist/esm/src/multer/multipart/handlers/any-files.js +29 -0
- package/dist/esm/src/multer/multipart/handlers/any-files.js.map +1 -0
- package/dist/esm/src/multer/multipart/handlers/file-fields.d.ts +14 -0
- package/dist/esm/src/multer/multipart/handlers/file-fields.js +52 -0
- package/dist/esm/src/multer/multipart/handlers/file-fields.js.map +1 -0
- package/dist/esm/src/multer/multipart/handlers/index.d.ts +1 -0
- package/dist/esm/src/multer/multipart/handlers/index.js +2 -0
- package/dist/esm/src/multer/multipart/handlers/index.js.map +1 -0
- package/dist/esm/src/multer/multipart/handlers/multiple-files.d.ts +8 -0
- package/dist/esm/src/multer/multipart/handlers/multiple-files.js +36 -0
- package/dist/esm/src/multer/multipart/handlers/multiple-files.js.map +1 -0
- package/dist/esm/src/multer/multipart/handlers/single-file.d.ts +8 -0
- package/dist/esm/src/multer/multipart/handlers/single-file.js +41 -0
- package/dist/esm/src/multer/multipart/handlers/single-file.js.map +1 -0
- package/dist/esm/src/multer/multipart/index.d.ts +3 -0
- package/dist/esm/src/multer/multipart/index.js +3 -0
- package/dist/esm/src/multer/multipart/index.js.map +1 -0
- package/dist/esm/src/multer/multipart/options.d.ts +22 -0
- package/dist/esm/src/multer/multipart/options.js +19 -0
- package/dist/esm/src/multer/multipart/options.js.map +1 -0
- package/dist/esm/src/multer/multipart/request.d.ts +16 -0
- package/dist/esm/src/multer/multipart/request.js +20 -0
- package/dist/esm/src/multer/multipart/request.js.map +1 -0
- package/dist/esm/src/multer/storage/disk-storage.d.ts +31 -0
- package/dist/esm/src/multer/storage/disk-storage.js +58 -0
- package/dist/esm/src/multer/storage/disk-storage.js.map +1 -0
- package/dist/esm/src/multer/storage/index.d.ts +3 -0
- package/dist/esm/src/multer/storage/index.js +4 -0
- package/dist/esm/src/multer/storage/index.js.map +1 -0
- package/dist/esm/src/multer/storage/memory-storage.d.ts +17 -0
- package/dist/esm/src/multer/storage/memory-storage.js +17 -0
- package/dist/esm/src/multer/storage/memory-storage.js.map +1 -0
- package/dist/esm/src/multer/storage/storage.d.ts +13 -0
- package/dist/esm/src/multer/storage/storage.js +2 -0
- package/dist/esm/src/multer/storage/storage.js.map +1 -0
- package/dist/esm/src/multer/stream/index.d.ts +3 -0
- package/dist/esm/src/multer/stream/index.js +4 -0
- package/dist/esm/src/multer/stream/index.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/index.ts +1 -0
- package/package.json +3 -1
- package/src/adapters/hono-adapter.ts +51 -50
- package/src/drivers/constants/apollo.constants.ts +1 -0
- package/src/drivers/constants/index.ts +1 -0
- package/src/drivers/graphQLUpload/GraphQLUpload.ts +86 -0
- package/src/drivers/graphQLUpload/Upload.ts +35 -0
- package/src/drivers/graphQLUpload/fs-capacitor.ts +285 -0
- package/src/drivers/graphQLUpload/index.ts +4 -0
- package/src/drivers/graphQLUpload/processRequest.ts +50 -0
- package/src/drivers/graphql.driver.ts +50 -6
- package/src/drivers/index.ts +1 -0
- package/src/drivers/services/plugins-explorer.service.ts +27 -0
- package/src/multer/crypto/index.ts +4 -0
- package/src/multer/decorators/index.ts +2 -0
- package/src/multer/decorators/uploaded-file-decorator.ts +12 -0
- package/src/multer/decorators/uploaded-files-decorator.ts +15 -0
- package/src/multer/fs/index.ts +22 -0
- package/src/multer/index.ts +4 -0
- package/src/multer/interceptors/any-files-interceptor.ts +46 -0
- package/src/multer/interceptors/file-fields-interceptor.ts +56 -0
- package/src/multer/interceptors/file-interceptor.ts +48 -0
- package/src/multer/interceptors/files-interceptor.ts +50 -0
- package/src/multer/interceptors/index.ts +4 -0
- package/src/multer/multipart/exceptions.ts +25 -0
- package/src/multer/multipart/file.ts +18 -0
- package/src/multer/multipart/filter.ts +38 -0
- package/src/multer/multipart/handlers/any-files.ts +39 -0
- package/src/multer/multipart/handlers/file-fields.ts +89 -0
- package/src/multer/multipart/handlers/index.ts +1 -0
- package/src/multer/multipart/handlers/multiple-files.ts +55 -0
- package/src/multer/multipart/handlers/single-file.ts +56 -0
- package/src/multer/multipart/index.ts +3 -0
- package/src/multer/multipart/options.ts +29 -0
- package/src/multer/multipart/request.ts +44 -0
- package/src/multer/storage/disk-storage.ts +107 -0
- package/src/multer/storage/index.ts +3 -0
- package/src/multer/storage/memory-storage.ts +25 -0
- package/src/multer/storage/storage.ts +15 -0
- package/src/multer/stream/index.ts +4 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Context } from "hono";
|
|
2
|
+
import { WriteStream, Upload } from ".";
|
|
3
|
+
import { Readable } from "stream";
|
|
4
|
+
|
|
5
|
+
export async function processRequest(
|
|
6
|
+
ctx: Context
|
|
7
|
+
): Promise<Record<string, any>> {
|
|
8
|
+
const body = await ctx.req.parseBody();
|
|
9
|
+
const operations = JSON.parse(body.operations as string);
|
|
10
|
+
const map = new Map(Object.entries(JSON.parse(body.map as string)));
|
|
11
|
+
|
|
12
|
+
for (const [fieldName, file] of Object.entries(body)) {
|
|
13
|
+
if (
|
|
14
|
+
fieldName === "operations" ||
|
|
15
|
+
fieldName === "map" ||
|
|
16
|
+
!(file instanceof File)
|
|
17
|
+
)
|
|
18
|
+
continue;
|
|
19
|
+
|
|
20
|
+
const fileKeys = map.get(fieldName);
|
|
21
|
+
if (!Array.isArray(fileKeys) || !fileKeys.length) continue;
|
|
22
|
+
|
|
23
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
24
|
+
const capacitor = new WriteStream();
|
|
25
|
+
Readable.from(buffer).pipe(capacitor);
|
|
26
|
+
|
|
27
|
+
const upload = new Upload();
|
|
28
|
+
upload.file = {
|
|
29
|
+
filename: file.name,
|
|
30
|
+
mimetype: file.type,
|
|
31
|
+
fieldName,
|
|
32
|
+
encoding: "7bit",
|
|
33
|
+
createReadStream: (options) => capacitor.createReadStream(options),
|
|
34
|
+
capacitor,
|
|
35
|
+
};
|
|
36
|
+
upload.resolve(upload.file);
|
|
37
|
+
|
|
38
|
+
for (const fileKey of fileKeys) {
|
|
39
|
+
const pathSegments = fileKey.split(".");
|
|
40
|
+
let current = operations;
|
|
41
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
42
|
+
if (!current[pathSegments[i]]) current[pathSegments[i]] = {};
|
|
43
|
+
current = current[pathSegments[i]];
|
|
44
|
+
}
|
|
45
|
+
current[pathSegments[pathSegments.length - 1]] = upload;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return operations;
|
|
50
|
+
}
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { ApolloServer, BaseContext, HeaderMap } from "@apollo/server";
|
|
2
|
-
import {
|
|
2
|
+
import { ModulesContainer } from "@nestjs/core";
|
|
3
|
+
import {
|
|
4
|
+
AbstractGraphQLDriver,
|
|
5
|
+
GqlSubscriptionService,
|
|
6
|
+
SubscriptionConfig,
|
|
7
|
+
} from "@nestjs/graphql";
|
|
3
8
|
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
|
|
4
9
|
import { ApolloDriverConfig } from "@nestjs/apollo";
|
|
5
10
|
import { Context, HonoRequest } from "hono";
|
|
6
11
|
import { StatusCode } from "hono/utils/http-status";
|
|
7
12
|
import { Logger } from "@nestjs/common";
|
|
13
|
+
import http from "http";
|
|
14
|
+
|
|
15
|
+
import { PluginsExplorerService } from "./services/plugins-explorer.service";
|
|
16
|
+
import { processRequest } from "./graphQLUpload";
|
|
8
17
|
|
|
9
18
|
export class HonoGraphQLDriver<
|
|
10
19
|
T extends Record<string, any> = ApolloDriverConfig
|
|
11
20
|
> extends AbstractGraphQLDriver {
|
|
12
21
|
protected apolloServer: ApolloServer<BaseContext>;
|
|
22
|
+
private _subscriptionService?: GqlSubscriptionService;
|
|
23
|
+
private readonly pluginsExplorerService: PluginsExplorerService;
|
|
24
|
+
|
|
25
|
+
constructor(modulesContainer: ModulesContainer) {
|
|
26
|
+
super();
|
|
27
|
+
this.pluginsExplorerService = new PluginsExplorerService(modulesContainer);
|
|
28
|
+
}
|
|
13
29
|
|
|
14
30
|
get instance(): ApolloServer<BaseContext> {
|
|
15
31
|
return this.apolloServer;
|
|
@@ -23,7 +39,21 @@ export class HonoGraphQLDriver<
|
|
|
23
39
|
throw new Error("This driver is only compatible with the Hono platform");
|
|
24
40
|
}
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
await this.registerHono(options);
|
|
43
|
+
|
|
44
|
+
if (options.installSubscriptionHandlers || options.subscriptions) {
|
|
45
|
+
const subscriptionsOptions: SubscriptionConfig =
|
|
46
|
+
options.subscriptions || { "subscriptions-transport-ws": {} };
|
|
47
|
+
this._subscriptionService = new GqlSubscriptionService(
|
|
48
|
+
{
|
|
49
|
+
schema: options.schema,
|
|
50
|
+
path: options.path,
|
|
51
|
+
context: options.context,
|
|
52
|
+
...subscriptionsOptions,
|
|
53
|
+
},
|
|
54
|
+
this.httpAdapterHost.httpAdapter?.getHttpServer()
|
|
55
|
+
);
|
|
56
|
+
}
|
|
27
57
|
}
|
|
28
58
|
|
|
29
59
|
protected async registerHono(
|
|
@@ -50,7 +80,7 @@ export class HonoGraphQLDriver<
|
|
|
50
80
|
await server.start();
|
|
51
81
|
|
|
52
82
|
app.use(path, async (ctx: Context) => {
|
|
53
|
-
const bodyData = await this.parseBody(ctx
|
|
83
|
+
const bodyData = await this.parseBody(ctx);
|
|
54
84
|
|
|
55
85
|
const defaultContext = () => Promise.resolve({} as BaseContext);
|
|
56
86
|
|
|
@@ -101,8 +131,9 @@ export class HonoGraphQLDriver<
|
|
|
101
131
|
this.apolloServer = server;
|
|
102
132
|
}
|
|
103
133
|
|
|
104
|
-
public stop() {
|
|
105
|
-
|
|
134
|
+
public async stop() {
|
|
135
|
+
await this._subscriptionService?.stop();
|
|
136
|
+
await this.apolloServer?.stop();
|
|
106
137
|
}
|
|
107
138
|
|
|
108
139
|
private httpHeadersToMap(headers: Headers) {
|
|
@@ -111,17 +142,30 @@ export class HonoGraphQLDriver<
|
|
|
111
142
|
return map;
|
|
112
143
|
}
|
|
113
144
|
|
|
114
|
-
private async parseBody(
|
|
145
|
+
private async parseBody(ctx: Context): Promise<Record<string, unknown>> {
|
|
146
|
+
const req = ctx.req;
|
|
115
147
|
const contentType = req.header("content-type");
|
|
148
|
+
|
|
116
149
|
if (contentType === "application/graphql")
|
|
117
150
|
return { query: await req.text() };
|
|
118
151
|
if (contentType === "application/json")
|
|
119
152
|
return req.json().catch(this.logError);
|
|
120
153
|
if (contentType === "application/x-www-form-urlencoded")
|
|
121
154
|
return this.parseFormURL(req);
|
|
155
|
+
if (contentType?.startsWith("multipart/form-data")) {
|
|
156
|
+
return processRequest(ctx);
|
|
157
|
+
}
|
|
122
158
|
return {};
|
|
123
159
|
}
|
|
124
160
|
|
|
161
|
+
headersToRecord(headers: Headers): http.IncomingHttpHeaders {
|
|
162
|
+
const obj: http.IncomingHttpHeaders = {};
|
|
163
|
+
headers.forEach((value, key) => {
|
|
164
|
+
obj[key] = value;
|
|
165
|
+
});
|
|
166
|
+
return obj;
|
|
167
|
+
}
|
|
168
|
+
|
|
125
169
|
private logError(e: unknown): void {
|
|
126
170
|
if (e instanceof Error) {
|
|
127
171
|
Logger.error(e.stack || e.message);
|
package/src/drivers/index.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
|
2
|
+
import { ModulesContainer } from '@nestjs/core/injector/modules-container';
|
|
3
|
+
import { BaseExplorerService, GqlModuleOptions } from '@nestjs/graphql';
|
|
4
|
+
import { PLUGIN_METADATA } from '../constants';
|
|
5
|
+
|
|
6
|
+
export class PluginsExplorerService extends BaseExplorerService {
|
|
7
|
+
constructor(private readonly modulesContainer: ModulesContainer) {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
explore(options: GqlModuleOptions) {
|
|
12
|
+
const modules = this.getModules(
|
|
13
|
+
this.modulesContainer,
|
|
14
|
+
options.include || [],
|
|
15
|
+
);
|
|
16
|
+
return this.flatMap(modules, (instance) => this.filterPlugins(instance));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
filterPlugins<T = any>(wrapper: InstanceWrapper<T>) {
|
|
20
|
+
const { instance } = wrapper;
|
|
21
|
+
if (!instance) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const metadata = Reflect.getMetadata(PLUGIN_METADATA, instance.constructor);
|
|
25
|
+
return metadata ? instance : undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
4
|
+
import { StorageFile } from '../storage/storage';
|
|
5
|
+
|
|
6
|
+
export const UploadedFile = createParamDecorator(
|
|
7
|
+
async (_data, ctx: ExecutionContext): Promise<StorageFile | undefined> => {
|
|
8
|
+
const req = getMultipartRequest(ctx.switchToHttp());
|
|
9
|
+
|
|
10
|
+
return req?.storageFile;
|
|
11
|
+
},
|
|
12
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
4
|
+
import { StorageFile } from '../storage/storage';
|
|
5
|
+
|
|
6
|
+
export const UploadedFiles = createParamDecorator(
|
|
7
|
+
async (
|
|
8
|
+
_data: any,
|
|
9
|
+
ctx: ExecutionContext,
|
|
10
|
+
): Promise<Record<string, StorageFile[]> | StorageFile[] | undefined> => {
|
|
11
|
+
const req = getMultipartRequest(ctx.switchToHttp());
|
|
12
|
+
|
|
13
|
+
return req?.storageFiles;
|
|
14
|
+
},
|
|
15
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { randomBytes } from '../crypto';
|
|
5
|
+
|
|
6
|
+
export const pathExists = async (path: string) => {
|
|
7
|
+
try {
|
|
8
|
+
await fs.stat(path);
|
|
9
|
+
} catch (err) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return true;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getUniqueFilename = async (filename: string) => {
|
|
17
|
+
const buffer = await randomBytes(16);
|
|
18
|
+
|
|
19
|
+
const ext = extname(filename);
|
|
20
|
+
|
|
21
|
+
return buffer.toString('hex') + ext;
|
|
22
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Observable, tap } from 'rxjs';
|
|
2
|
+
import {
|
|
3
|
+
CallHandler,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
mixin,
|
|
6
|
+
NestInterceptor,
|
|
7
|
+
Type,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
|
|
10
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
11
|
+
import { transformUploadOptions, UploadOptions } from '../multipart/options';
|
|
12
|
+
import { handleMultipartAnyFiles } from '../multipart/handlers/any-files';
|
|
13
|
+
|
|
14
|
+
export function AnyFilesInterceptor(
|
|
15
|
+
options?: UploadOptions,
|
|
16
|
+
): Type<NestInterceptor> {
|
|
17
|
+
class MixinInterceptor implements NestInterceptor {
|
|
18
|
+
private readonly options: UploadOptions;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.options = transformUploadOptions(options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async intercept(
|
|
25
|
+
context: ExecutionContext,
|
|
26
|
+
next: CallHandler,
|
|
27
|
+
): Promise<Observable<any>> {
|
|
28
|
+
const ctx = context.switchToHttp();
|
|
29
|
+
const req = getMultipartRequest(ctx);
|
|
30
|
+
|
|
31
|
+
const { body, files, remove } = await handleMultipartAnyFiles(
|
|
32
|
+
req,
|
|
33
|
+
this.options,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
req.body = body;
|
|
37
|
+
req.storageFiles = files;
|
|
38
|
+
|
|
39
|
+
return next.handle().pipe(tap(remove));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Interceptor = mixin(MixinInterceptor);
|
|
44
|
+
|
|
45
|
+
return Interceptor;
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Observable, tap } from 'rxjs';
|
|
2
|
+
import {
|
|
3
|
+
CallHandler,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
mixin,
|
|
6
|
+
NestInterceptor,
|
|
7
|
+
Type,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
|
|
10
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
11
|
+
import { transformUploadOptions, UploadOptions } from '../multipart/options';
|
|
12
|
+
import {
|
|
13
|
+
handleMultipartFileFields,
|
|
14
|
+
UploadField,
|
|
15
|
+
UploadFieldMapEntry,
|
|
16
|
+
uploadFieldsToMap,
|
|
17
|
+
} from '../multipart/handlers/file-fields';
|
|
18
|
+
|
|
19
|
+
export function FileFieldsInterceptor(
|
|
20
|
+
uploadFields: UploadField[],
|
|
21
|
+
options?: UploadOptions,
|
|
22
|
+
): Type<NestInterceptor> {
|
|
23
|
+
class MixinInterceptor implements NestInterceptor {
|
|
24
|
+
private readonly options: UploadOptions;
|
|
25
|
+
|
|
26
|
+
private readonly fieldsMap: Map<string, UploadFieldMapEntry>;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
this.options = transformUploadOptions(options);
|
|
30
|
+
this.fieldsMap = uploadFieldsToMap(uploadFields);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async intercept(
|
|
34
|
+
context: ExecutionContext,
|
|
35
|
+
next: CallHandler,
|
|
36
|
+
): Promise<Observable<any>> {
|
|
37
|
+
const ctx = context.switchToHttp();
|
|
38
|
+
const req = getMultipartRequest(ctx);
|
|
39
|
+
|
|
40
|
+
const { body, files, remove } = await handleMultipartFileFields(
|
|
41
|
+
req,
|
|
42
|
+
this.fieldsMap,
|
|
43
|
+
this.options,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
req.body = body;
|
|
47
|
+
req.storageFiles = files;
|
|
48
|
+
|
|
49
|
+
return next.handle().pipe(tap(remove));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const Interceptor = mixin(MixinInterceptor);
|
|
54
|
+
|
|
55
|
+
return Interceptor;
|
|
56
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Observable, tap } from 'rxjs';
|
|
2
|
+
import {
|
|
3
|
+
CallHandler,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
mixin,
|
|
6
|
+
NestInterceptor,
|
|
7
|
+
Type,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
|
|
10
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
11
|
+
import { transformUploadOptions, UploadOptions } from '../multipart/options';
|
|
12
|
+
import { handleMultipartSingleFile } from '../multipart/handlers/single-file';
|
|
13
|
+
|
|
14
|
+
export function FileInterceptor(
|
|
15
|
+
fieldname: string,
|
|
16
|
+
options?: UploadOptions,
|
|
17
|
+
): Type<NestInterceptor> {
|
|
18
|
+
class MixinInterceptor implements NestInterceptor {
|
|
19
|
+
private readonly options: UploadOptions;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
this.options = transformUploadOptions(options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async intercept(
|
|
26
|
+
context: ExecutionContext,
|
|
27
|
+
next: CallHandler,
|
|
28
|
+
): Promise<Observable<any>> {
|
|
29
|
+
const ctx = context.switchToHttp();
|
|
30
|
+
const req = getMultipartRequest(ctx);
|
|
31
|
+
|
|
32
|
+
const { file, body, remove } = await handleMultipartSingleFile(
|
|
33
|
+
req,
|
|
34
|
+
fieldname,
|
|
35
|
+
this.options,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
req.body = body;
|
|
39
|
+
req.storageFile = file;
|
|
40
|
+
|
|
41
|
+
return next.handle().pipe(tap(remove));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const Interceptor = mixin(MixinInterceptor);
|
|
46
|
+
|
|
47
|
+
return Interceptor;
|
|
48
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Observable, tap } from 'rxjs';
|
|
2
|
+
import {
|
|
3
|
+
CallHandler,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
mixin,
|
|
6
|
+
NestInterceptor,
|
|
7
|
+
Type,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
|
|
10
|
+
import { getMultipartRequest } from '../multipart/request';
|
|
11
|
+
import { transformUploadOptions, UploadOptions } from '../multipart/options';
|
|
12
|
+
import { handleMultipartMultipleFiles } from '../multipart/handlers/multiple-files';
|
|
13
|
+
|
|
14
|
+
export function FilesInterceptor(
|
|
15
|
+
fieldname: string,
|
|
16
|
+
maxCount = 1,
|
|
17
|
+
options?: UploadOptions,
|
|
18
|
+
): Type<NestInterceptor> {
|
|
19
|
+
class MixinInterceptor implements NestInterceptor {
|
|
20
|
+
private readonly options: UploadOptions;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
this.options = transformUploadOptions(options);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async intercept(
|
|
27
|
+
context: ExecutionContext,
|
|
28
|
+
next: CallHandler,
|
|
29
|
+
): Promise<Observable<any>> {
|
|
30
|
+
const ctx = context.switchToHttp();
|
|
31
|
+
const req = getMultipartRequest(ctx);
|
|
32
|
+
|
|
33
|
+
const { body, files, remove } = await handleMultipartMultipleFiles(
|
|
34
|
+
req,
|
|
35
|
+
fieldname,
|
|
36
|
+
maxCount,
|
|
37
|
+
this.options,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
req.body = body;
|
|
41
|
+
req.storageFiles = files;
|
|
42
|
+
|
|
43
|
+
return next.handle().pipe(tap(remove));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const Interceptor = mixin(MixinInterceptor);
|
|
48
|
+
|
|
49
|
+
return Interceptor;
|
|
50
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BadRequestException,
|
|
3
|
+
HttpException,
|
|
4
|
+
PayloadTooLargeException,
|
|
5
|
+
} from '@nestjs/common';
|
|
6
|
+
|
|
7
|
+
export const transformException = (err: Error | undefined) => {
|
|
8
|
+
if (!err || err instanceof HttpException) {
|
|
9
|
+
return err;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const code: string = (err as any).code;
|
|
13
|
+
|
|
14
|
+
switch (code) {
|
|
15
|
+
case 'REQ_FILE_TOO_LARGE':
|
|
16
|
+
return new PayloadTooLargeException();
|
|
17
|
+
case 'PARTS_LIMIT':
|
|
18
|
+
case 'FILES_LIMIT':
|
|
19
|
+
case 'PROTO_VIOLATION':
|
|
20
|
+
case 'INVALID_MULTIPART_CONTENT_TYPE':
|
|
21
|
+
return new BadRequestException(err.message);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return err;
|
|
25
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
import { Storage, StorageFile } from '../storage';
|
|
3
|
+
|
|
4
|
+
export type MultipartFile = Omit<File[], 'file'> & {
|
|
5
|
+
value?: any;
|
|
6
|
+
file: Readable & { truncated?: boolean };
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const removeStorageFiles = async (
|
|
10
|
+
storage: Storage,
|
|
11
|
+
files?: (StorageFile | undefined)[],
|
|
12
|
+
force?: boolean,
|
|
13
|
+
) => {
|
|
14
|
+
if (files == null) return;
|
|
15
|
+
await Promise.all(
|
|
16
|
+
files.map((file) => file && storage.removeFile(file, force)),
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BadRequestException } from '@nestjs/common';
|
|
2
|
+
import { UploadOptions } from '.';
|
|
3
|
+
|
|
4
|
+
import { DiskStorageFile, MemoryStorageFile, StorageFile } from '../storage';
|
|
5
|
+
import { HonoRequest } from 'hono';
|
|
6
|
+
|
|
7
|
+
export type UploadFilterFile =
|
|
8
|
+
| DiskStorageFile
|
|
9
|
+
| MemoryStorageFile
|
|
10
|
+
| StorageFile;
|
|
11
|
+
|
|
12
|
+
export type UploadFilterHandler = (
|
|
13
|
+
req: HonoRequest,
|
|
14
|
+
file: UploadFilterFile,
|
|
15
|
+
) => Promise<boolean | string> | boolean | string;
|
|
16
|
+
|
|
17
|
+
export const filterUpload = async (
|
|
18
|
+
uploadOptions: UploadOptions,
|
|
19
|
+
req: HonoRequest,
|
|
20
|
+
file: UploadFilterFile,
|
|
21
|
+
): Promise<boolean> => {
|
|
22
|
+
if (uploadOptions.filter == null) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const res = await uploadOptions.filter(req, file);
|
|
28
|
+
|
|
29
|
+
if (typeof res === 'string') {
|
|
30
|
+
throw new BadRequestException(res);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return res;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
await uploadOptions.storage!.removeFile(file, true);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UploadOptions } from '../options';
|
|
2
|
+
import { StorageFile } from '../../storage';
|
|
3
|
+
import { THonoRequest, getParts } from '../request';
|
|
4
|
+
import { removeStorageFiles } from '../file';
|
|
5
|
+
import { filterUpload } from '../filter';
|
|
6
|
+
|
|
7
|
+
export const handleMultipartAnyFiles = async (
|
|
8
|
+
req: THonoRequest,
|
|
9
|
+
options: UploadOptions,
|
|
10
|
+
) => {
|
|
11
|
+
const parts = await getParts(req, options);
|
|
12
|
+
|
|
13
|
+
const body: Record<string, any> = {};
|
|
14
|
+
|
|
15
|
+
const files: StorageFile[] = [];
|
|
16
|
+
|
|
17
|
+
const removeFiles = async (error?: boolean) => {
|
|
18
|
+
return await removeStorageFiles(options.storage!, files, error);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
for await (const [partFieldName, part] of Object.entries(parts)) {
|
|
23
|
+
if (!(part instanceof File)) {
|
|
24
|
+
body[partFieldName] = part;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const file = await options.storage!.handleFile(part, req, partFieldName);
|
|
28
|
+
|
|
29
|
+
if (await filterUpload(options, req, file)) {
|
|
30
|
+
files.push(file);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
await removeFiles(true);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { body, files, remove: () => removeFiles() };
|
|
39
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { BadRequestException } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { UploadOptions } from '../options';
|
|
4
|
+
import { StorageFile } from '../../storage/storage';
|
|
5
|
+
import { THonoRequest, getParts } from '../request';
|
|
6
|
+
import { removeStorageFiles } from '../file';
|
|
7
|
+
import { filterUpload } from '../filter';
|
|
8
|
+
import { HonoRequest } from 'hono';
|
|
9
|
+
|
|
10
|
+
export interface UploadField {
|
|
11
|
+
/**
|
|
12
|
+
* Field name
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* Max number of files in this field
|
|
17
|
+
*/
|
|
18
|
+
maxCount?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type UploadFieldMapEntry = Required<Pick<UploadField, 'maxCount'>>;
|
|
22
|
+
|
|
23
|
+
export const uploadFieldsToMap = (uploadFields: UploadField[]) => {
|
|
24
|
+
const map = new Map<string, UploadFieldMapEntry>();
|
|
25
|
+
|
|
26
|
+
uploadFields.forEach(({ name, ...opts }) => {
|
|
27
|
+
map.set(name, { maxCount: 1, ...opts });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return map;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const handleMultipartFileFields = async (
|
|
34
|
+
req: THonoRequest,
|
|
35
|
+
fieldsMap: Map<string, UploadFieldMapEntry>,
|
|
36
|
+
options: UploadOptions,
|
|
37
|
+
) => {
|
|
38
|
+
const parts = await getParts(req, options);
|
|
39
|
+
const body: Record<string, any> = {};
|
|
40
|
+
|
|
41
|
+
const files: Record<string, StorageFile[]> = {};
|
|
42
|
+
|
|
43
|
+
const removeFiles = async (error?: boolean) => {
|
|
44
|
+
const allFiles = ([] as StorageFile[]).concat(...Object.values(files));
|
|
45
|
+
return await removeStorageFiles(options.storage!, allFiles, error);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
for await (const [fieldName, part] of Object.entries(parts)) {
|
|
50
|
+
if (!(part instanceof File)) {
|
|
51
|
+
body[fieldName] = part;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fieldOptions = fieldsMap.get(fieldName);
|
|
56
|
+
|
|
57
|
+
if (fieldOptions == null) {
|
|
58
|
+
throw new BadRequestException(
|
|
59
|
+
`Field ${fieldName} doesn't accept files`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (files[fieldName] == null) {
|
|
64
|
+
files[fieldName] = [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (files[fieldName].length + 1 > fieldOptions.maxCount) {
|
|
68
|
+
throw new BadRequestException(
|
|
69
|
+
`Field ${fieldName} accepts max ${fieldOptions.maxCount} files`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const file = await options.storage!.handleFile(part, req, fieldName);
|
|
74
|
+
|
|
75
|
+
if (await filterUpload(options, req, file)) {
|
|
76
|
+
files[fieldName].push(file);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
await removeFiles(true);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
body,
|
|
86
|
+
files,
|
|
87
|
+
remove: () => removeFiles(),
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { UploadField } from "./file-fields";
|