53 lines
2.8 KiB
TypeScript
53 lines
2.8 KiB
TypeScript
type AssetKind = 'document' | 'spreadsheet' | 'archive';
|
|
|
|
type AssetDefinition = {
|
|
kind: AssetKind;
|
|
extension: string;
|
|
needsZipSignature?: boolean;
|
|
needsOleSignature?: boolean;
|
|
needsPdfSignature?: boolean;
|
|
needsTextContent?: boolean;
|
|
};
|
|
|
|
const allowedAssetTypes = new Map<string, AssetDefinition>([
|
|
['application/pdf', { kind: 'document', extension: 'pdf', needsPdfSignature: true }],
|
|
['application/vnd.openxmlformats-officedocument.wordprocessingml.document', { kind: 'document', extension: 'docx', needsZipSignature: true }],
|
|
['application/msword', { kind: 'document', extension: 'doc', needsOleSignature: true }],
|
|
['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', { kind: 'spreadsheet', extension: 'xlsx', needsZipSignature: true }],
|
|
['application/vnd.ms-excel', { kind: 'spreadsheet', extension: 'xls', needsOleSignature: true }],
|
|
['text/csv', { kind: 'spreadsheet', extension: 'csv', needsTextContent: true }],
|
|
['application/zip', { kind: 'archive', extension: 'zip', needsZipSignature: true }],
|
|
]);
|
|
|
|
export class AssetUploadValidationError extends Error {
|
|
constructor(message: string, public readonly statusCode: 400 | 413 | 415) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
const zipSignature = Buffer.from([0x50, 0x4b, 0x03, 0x04]);
|
|
const oleSignature = Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]);
|
|
|
|
export function validateAssetUpload(body: Buffer, contentType: string | undefined, maxBytes: number) {
|
|
const normalizedType = contentType?.split(';')[0]?.trim().toLowerCase();
|
|
const definition = normalizedType ? allowedAssetTypes.get(normalizedType) : undefined;
|
|
if (!definition) {
|
|
throw new AssetUploadValidationError('Envie apenas arquivos PDF, DOCX, XLSX, CSV ou ZIP.', 415);
|
|
}
|
|
if (body.length === 0) throw new AssetUploadValidationError('Escolha um arquivo para enviar.', 400);
|
|
if (body.length > maxBytes) throw new AssetUploadValidationError(`O arquivo excede o limite de ${Math.floor(maxBytes / 1024 / 1024)} MB.`, 413);
|
|
if (definition.needsPdfSignature && !body.subarray(0, 5).equals(Buffer.from('%PDF-'))) {
|
|
throw new AssetUploadValidationError('O arquivo enviado não é um PDF válido.', 415);
|
|
}
|
|
if (definition.needsZipSignature && !body.subarray(0, 4).equals(zipSignature)) {
|
|
throw new AssetUploadValidationError('O arquivo enviado não possui o formato esperado.', 415);
|
|
}
|
|
if (definition.needsOleSignature && !body.subarray(0, 8).equals(oleSignature)) {
|
|
throw new AssetUploadValidationError('O arquivo enviado não possui o formato esperado.', 415);
|
|
}
|
|
if (definition.needsTextContent && body.subarray(0, Math.min(body.length, 4096)).includes(0)) {
|
|
throw new AssetUploadValidationError('O arquivo CSV deve conter texto.', 415);
|
|
}
|
|
return { kind: definition.kind, extension: definition.extension, contentType: normalizedType as string };
|
|
}
|