FEAT - OPS ATTACHMENTS: multi-format upload, stable card drop zone and text previews

This commit is contained in:
DCCONSTRUCTIONS
2026-08-29 16:08:02 +03:00
parent ca21a8f5bc
commit f9308539bf
5 changed files with 423 additions and 22 deletions
+38 -5
View File
@@ -10,6 +10,30 @@ import { fileTypeFromBuffer } from "file-type";
import type { TFileMetaDataLite, TFileSignedURLResponse } from "@plane/types";
import { DANGEROUS_EXTENSIONS } from "@plane/constants";
const TEXT_MIME_TYPES_BY_EXTENSION: Readonly<Record<string, string>> = {
cfg: "text/plain",
conf: "text/plain",
csv: "text/csv",
ini: "text/plain",
json: "application/json",
jsonl: "application/x-ndjson",
log: "text/plain",
markdown: "text/markdown",
md: "text/markdown",
mdown: "text/markdown",
mkd: "text/markdown",
mkdn: "text/markdown",
ndjson: "application/x-ndjson",
toml: "application/toml",
tsv: "text/tab-separated-values",
txt: "text/plain",
xml: "application/xml",
yaml: "application/yaml",
yml: "application/yaml",
};
const getFileExtension = (filename: string): string => filename.split(".").pop()?.trim().toLowerCase() ?? "";
/**
* @description Filename validation - checks for double extensions and dangerous patterns
* @param {string} filename
@@ -82,8 +106,10 @@ const detectMimeTypeFromSignature = async (file: File): Promise<string> => {
};
/**
* @description Validate and detect the MIME type of a file using signature detection
* Also performs basic security checks on filename
* @description Validate and detect the MIME type of a file.
* Binary signatures take precedence. Text formats are resolved from a conservative
* extension map because they do not have a binary signature. Browser MIME metadata
* and application/octet-stream provide compatibility for other safe attachments.
* @param {File} file
* @returns {Promise<string>} validated and detected MIME type
*/
@@ -91,7 +117,7 @@ const validateAndDetectFileType = async (file: File): Promise<string> => {
// Basic filename validation
const filenameError = validateFilename(file.name);
if (filenameError) {
console.warn(`File validation warning: ${filenameError}`);
throw new Error(filenameError);
}
try {
@@ -103,8 +129,15 @@ const validateAndDetectFileType = async (file: File): Promise<string> => {
console.warn("Error detecting file type from signature:", _error);
}
// fallback for unknown files
return "";
const extensionMimeType = TEXT_MIME_TYPES_BY_EXTENSION[getFileExtension(file.name)];
if (extensionMimeType) return extensionMimeType;
const browserMimeType = file.type.split(";", 1)[0]?.trim().toLowerCase();
if (browserMimeType) return browserMimeType;
// Preserve generic attachment support when neither the file signature nor the
// browser can identify a safe filename. The API still enforces its MIME allowlist.
return "application/octet-stream";
};
/**