142 lines
4.9 KiB
TypeScript
142 lines
4.9 KiB
TypeScript
const BLOCKED_DOCUMENT_TYPES = new Set([
|
|
'application/javascript',
|
|
'application/x-javascript',
|
|
'text/javascript',
|
|
'text/html',
|
|
'application/xhtml+xml',
|
|
'application/x-httpd-php',
|
|
'application/x-sh'
|
|
]);
|
|
|
|
const EXTENSION_TO_MIME: Record<string, string> = {
|
|
jpg: 'image/jpeg',
|
|
jpeg: 'image/jpeg',
|
|
png: 'image/png',
|
|
webp: 'image/webp',
|
|
gif: 'image/gif',
|
|
pdf: 'application/pdf',
|
|
doc: 'application/msword',
|
|
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
xls: 'application/vnd.ms-excel',
|
|
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
ppt: 'application/vnd.ms-powerpoint',
|
|
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
txt: 'text/plain',
|
|
rtf: 'application/rtf',
|
|
odt: 'application/vnd.oasis.opendocument.text'
|
|
};
|
|
|
|
const ALLOWED_DOCUMENT_MIME = new Set([
|
|
'image/jpeg',
|
|
'image/png',
|
|
'image/webp',
|
|
'image/gif',
|
|
'application/pdf',
|
|
'application/msword',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
'application/vnd.ms-excel',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'application/vnd.ms-powerpoint',
|
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
'text/plain',
|
|
'application/rtf',
|
|
'application/vnd.oasis.opendocument.text'
|
|
]);
|
|
|
|
const MIME_ALIASES: Record<string, string> = {
|
|
'image/jpg': 'image/jpeg',
|
|
'image/pjpeg': 'image/jpeg',
|
|
'application/x-pdf': 'application/pdf'
|
|
};
|
|
|
|
function extractFileExtension(fileName?: string | null) {
|
|
const trimmed = (fileName ?? '').trim().toLowerCase();
|
|
const dotIndex = trimmed.lastIndexOf('.');
|
|
if (dotIndex <= 0) return '';
|
|
return trimmed.slice(dotIndex + 1);
|
|
}
|
|
|
|
export function normalizeDocumentContentType(contentType?: string | null, fileName?: string | null) {
|
|
const raw = (contentType ?? '').trim().toLowerCase();
|
|
const base = raw.split(';')[0]?.trim() ?? '';
|
|
const aliased = MIME_ALIASES[base] ?? base;
|
|
|
|
if (aliased && aliased !== 'application/octet-stream' && ALLOWED_DOCUMENT_MIME.has(aliased)) {
|
|
return aliased;
|
|
}
|
|
|
|
const extension = extractFileExtension(fileName);
|
|
if (extension && EXTENSION_TO_MIME[extension]) {
|
|
return EXTENSION_TO_MIME[extension];
|
|
}
|
|
|
|
return aliased || 'application/octet-stream';
|
|
}
|
|
|
|
export function assertDocumentAttachmentContentType(contentType: string, fileName?: string) {
|
|
const normalized = normalizeDocumentContentType(contentType, fileName);
|
|
if (BLOCKED_DOCUMENT_TYPES.has(normalized)) {
|
|
throw new Error('Тип файла не поддерживается');
|
|
}
|
|
if (!ALLOWED_DOCUMENT_MIME.has(normalized)) {
|
|
throw new Error('Допустимы изображения, PDF, Word, Excel, PowerPoint, TXT и ODT');
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
export function extensionForDocumentContentType(contentType: string, fileName?: string) {
|
|
const normalized = normalizeDocumentContentType(contentType, fileName);
|
|
switch (normalized) {
|
|
case 'image/jpeg':
|
|
return 'jpg';
|
|
case 'image/png':
|
|
return 'png';
|
|
case 'image/webp':
|
|
return 'webp';
|
|
case 'image/gif':
|
|
return 'gif';
|
|
case 'application/pdf':
|
|
return 'pdf';
|
|
case 'application/msword':
|
|
return 'doc';
|
|
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
|
return 'docx';
|
|
case 'application/vnd.ms-excel':
|
|
return 'xls';
|
|
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
|
return 'xlsx';
|
|
case 'application/vnd.ms-powerpoint':
|
|
return 'ppt';
|
|
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
|
return 'pptx';
|
|
case 'text/plain':
|
|
return 'txt';
|
|
case 'application/rtf':
|
|
return 'rtf';
|
|
case 'application/vnd.oasis.opendocument.text':
|
|
return 'odt';
|
|
default: {
|
|
const extension = extractFileExtension(fileName);
|
|
return extension || 'bin';
|
|
}
|
|
}
|
|
}
|
|
|
|
export function contentTypeForDocumentKey(storageKey: string) {
|
|
const lower = storageKey.toLowerCase();
|
|
if (lower.endsWith('.png')) return 'image/png';
|
|
if (lower.endsWith('.webp')) return 'image/webp';
|
|
if (lower.endsWith('.gif')) return 'image/gif';
|
|
if (lower.endsWith('.pdf')) return 'application/pdf';
|
|
if (lower.endsWith('.doc')) return 'application/msword';
|
|
if (lower.endsWith('.docx')) return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
if (lower.endsWith('.xls')) return 'application/vnd.ms-excel';
|
|
if (lower.endsWith('.xlsx')) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
if (lower.endsWith('.ppt')) return 'application/vnd.ms-powerpoint';
|
|
if (lower.endsWith('.pptx')) return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
if (lower.endsWith('.txt')) return 'text/plain';
|
|
if (lower.endsWith('.rtf')) return 'application/rtf';
|
|
if (lower.endsWith('.odt')) return 'application/vnd.oasis.opendocument.text';
|
|
return 'image/jpeg';
|
|
}
|