Files
dh-inspeccion-v2/api-v3/src/asset-master/asset-media-file.ts
T

73 lines
2.5 KiB
TypeScript

import { BadRequestException } from '@nestjs/common';
import { AssetMediaKind } from '../database/entities';
export const MAX_ASSET_MEDIA_BYTES = 15 * 1024 * 1024;
export interface UploadedAssetFile {
buffer: Buffer;
originalname: string;
mimetype?: string;
size: number;
}
export interface InspectedAssetFile {
originalName: string;
mimeType: 'image/jpeg' | 'image/png' | 'image/webp' | 'application/pdf';
extension: '.jpg' | '.png' | '.webp' | '.pdf';
}
function invalidFile(message: string): BadRequestException {
return new BadRequestException({ code: 'INVALID_ASSET_FILE', message });
}
function detectedType(buffer: Buffer): Omit<InspectedAssetFile, 'originalName'> | null {
if (buffer.length >= 4 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
return { mimeType: 'image/jpeg', extension: '.jpg' };
}
if (
buffer.length >= 8
&& buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
) {
return { mimeType: 'image/png', extension: '.png' };
}
if (
buffer.length >= 12
&& buffer.subarray(0, 4).toString('ascii') === 'RIFF'
&& buffer.subarray(8, 12).toString('ascii') === 'WEBP'
) {
return { mimeType: 'image/webp', extension: '.webp' };
}
if (buffer.length >= 5 && buffer.subarray(0, 5).toString('ascii') === '%PDF-') {
return { mimeType: 'application/pdf', extension: '.pdf' };
}
return null;
}
export function inspectAssetFile(
file: UploadedAssetFile | undefined,
kind: AssetMediaKind,
): InspectedAssetFile {
if (!file?.buffer || file.size <= 0 || file.buffer.length <= 0) {
throw invalidFile('Debe seleccionar un archivo no vacío');
}
if (file.size > MAX_ASSET_MEDIA_BYTES || file.buffer.length > MAX_ASSET_MEDIA_BYTES) {
throw invalidFile('El archivo supera el máximo permitido de 15 MB');
}
const detected = detectedType(file.buffer);
if (!detected) {
throw invalidFile('Sólo se permiten JPG, PNG, WebP y PDF válidos');
}
if (kind === AssetMediaKind.PHOTO && !detected.mimeType.startsWith('image/')) {
throw invalidFile('Una fotografía debe ser JPG, PNG o WebP');
}
if (kind === AssetMediaKind.DOCUMENT && detected.mimeType !== 'application/pdf') {
throw invalidFile('Un documento debe ser un archivo PDF');
}
const originalName = file.originalname
.replace(/[\u0000-\u001f\u007f]/g, '')
.trim()
.slice(0, 255);
if (!originalName) throw invalidFile('El nombre original del archivo no es válido');
return { ...detected, originalName };
}