Last active
July 5, 2023 13:54
-
-
Save SinanAkkoyun/1a72dabfd498e27f7d525b835d41c097 to your computer and use it in GitHub Desktop.
Curveball TS Multiform Middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Controller from '@curveball/controller' | |
import { Context } from '@curveball/core' | |
import Busboy, { FileInfo } from 'busboy' | |
import { IncomingHttpHeaders } from 'http' | |
import { Readable, Writable } from 'stream' | |
export interface MultiformFile { | |
name: string | |
info: FileInfo | |
stream?: NodeJS.ReadableStream | |
buffer?: Buffer | |
} | |
export interface CheckParamsOptions { | |
requiredParams?: string[] | |
requiredFiles?: string[] | |
} | |
export const checkParams = (ctx: Context, options: CheckParamsOptions) => { | |
const { requiredParams, requiredFiles } = options | |
if(!ctx.state.parameters || !ctx.state.files) throw new Error('Multiform middleware not used') | |
// check required params | |
for (const param of requiredParams || []) { | |
if (!ctx.state.parameters.has(param)) { | |
throw new Error(`Missing required parameter: ${param}`) | |
} | |
} | |
// check required files | |
for (const file of requiredFiles || []) { | |
if (!ctx.state.files.has(file)) { | |
throw new Error(`Missing required file: ${file}`) | |
} | |
} | |
} | |
export const multiformMiddleware = () => { | |
return async (ctx: Context, next: Function) => { | |
await new Promise<void>(async (resolve, reject) => { | |
if(ctx.request.headers.get('content-type')?.startsWith('multipart/form-data')) { | |
//ctx.state.params = new Map() | |
//ctx.state.files = new Map() | |
const headersObject = ctx.request.headers.getAll() | |
const headers: IncomingHttpHeaders = {} | |
for (const key in headersObject) { | |
const value = headersObject[key] | |
headers[key] = Array.isArray(value) ? value.map(String) : String(value) | |
} | |
const busboy = Busboy({ headers }) | |
//const params: Map<string, string> = new Map() | |
//const files: Map<string, MultiformFile> = new Map() | |
ctx.state.parameters = new Map() | |
ctx.state.files = new Map() | |
busboy.on('file', async function(fieldname: string, file: NodeJS.ReadableStream, info: FileInfo) { | |
console.log('file', info.filename) | |
//file.emit('end') | |
// instead of stream, load into buffer... (big files don't work with passing the stream) | |
let readStream = new Writable() | |
let buffer: Buffer = Buffer.alloc(0) | |
readStream._write = function(chunk, encoding, done) { | |
buffer = Buffer.concat([buffer, chunk]) | |
done() | |
} | |
file.pipe(readStream) | |
await new Promise<void>((resolve, reject) => { | |
file.on('end', () => { | |
resolve() | |
}) | |
}) | |
ctx.state.files.set(fieldname, { | |
name: info.filename, | |
info, | |
//stream: file | |
buffer | |
}) | |
console.log('filesize', buffer.length) | |
}) | |
busboy.on('field', function(fieldname: string, val: string) { | |
console.log('field', fieldname) | |
ctx.state.parameters.set(fieldname, val) | |
//console.log('params', ctx.state.params) | |
}) | |
busboy.on('error', function(err: Error) { | |
// console.log('Error parsing form: ' + err.stack) | |
ctx.state.error = err | |
}) | |
busboy.on('close', function() { | |
//ctx.state.params = params | |
//ctx.state.files = files | |
resolve() | |
}) | |
ctx.request.getStream().pipe(busboy) | |
} else { | |
resolve() | |
} | |
}) | |
console.log('pass to next') | |
return next() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment